RetryingInstaller { @NotNull public RetryingInstallerResult install() { InstallResult result = null; boolean retry = true; int retryCount = 0; StopWatch totalDuration = new StopWatch(); StopWatch lastInstallDuration = new StopWatch(); while (!myLaunchStatus.isLaunchTerminated() && retry) { retryCount++; lastInstallDuration.restart(); result = myInstaller.installApp(myDevice, myLaunchStatus); lastInstallDuration.stop(); if (result.installOutput != null) { if (result.failureCode == InstallResult.FailureCode.NO_ERROR) { myPrinter.stdout(result.installOutput); } else { myPrinter.stderr(result.installOutput); } } String reason; switch (result.failureCode) { case DEVICE_NOT_RESPONDING: int waitTime = 2; myPrinter.stdout("Device is not ready. Waiting for " + waitTime + " seconds."); try { TimeUnit.SECONDS.sleep(waitTime); } catch (InterruptedException e) { Logger.getInstance(RetryingInstaller.class).info(e); } retry = true; break; case INSTALL_FAILED_VERSION_DOWNGRADE: reason = AndroidBundle .message("deployment.failed.uninstall.prompt.text", AndroidBundle.message("deployment.failed.reason.version.downgrade")); retry = myPrompter.showQuestionPrompt(reason) && uninstallPackage(myDevice, myApplicationId); break; case INSTALL_FAILED_UPDATE_INCOMPATIBLE: case INCONSISTENT_CERTIFICATES: reason = AndroidBundle .message("deployment.failed.uninstall.prompt.text", AndroidBundle.message("deployment.failed.reason.different.signature")); retry = myPrompter.showQuestionPrompt(reason) && uninstallPackage(myDevice, myApplicationId); break; case INSTALL_FAILED_DEXOPT: reason = AndroidBundle.message("deployment.failed.uninstall.prompt.text", AndroidBundle.message("deployment.failed.reason.dexopt")); retry = myPrompter.showQuestionPrompt(reason) && uninstallPackage(myDevice, myApplicationId); break; case NO_CERTIFICATE: myPrinter.stderr(AndroidBundle.message("deployment.failed.no.certificates.explanation")); myPrompter.showErrorMessage(AndroidBundle.message("deployment.failed.no.certificates.explanation")); retry = false; break; case INSTALL_FAILED_OLDER_SDK: String message = AndroidBundle.message("deployment.failed.reason.oldersdk", myDevice.getVersion().toString()); myPrinter.stderr(message); retry = false; break; case DEVICE_NOT_FOUND: reason = AndroidBundle.message("deployment.failed.reason.devicedisconnected", myDevice.getName()); myPrompter.showErrorMessage(reason); retry = false; break; case UNTYPED_ERROR: reason = AndroidBundle.message("deployment.failed.uninstall.prompt.generic.text", result.failureMessage); retry = myPrompter.showQuestionPrompt(reason) && uninstallPackage(myDevice, myApplicationId); break; default: retry = false; break; } } totalDuration.stop(); return new RetryingInstallerResult(result != null && result.failureCode == InstallResult.FailureCode.NO_ERROR, retryCount, totalDuration.getDuration(), lastInstallDuration.getDuration()); } RetryingInstaller(@NotNull Project project,
                           @NotNull IDevice device,
                           @NotNull Installer installer,
                           @NotNull String applicationId,
                           @NotNull ConsolePrinter printer,
                           @NotNull LaunchStatus launchStatus); @VisibleForTesting  RetryingInstaller(@NotNull IDevice device,
                           @NotNull Installer installer,
                           @NotNull String applicationId,
                           @NotNull Prompter prompter,
                           @NotNull ConsolePrinter printer,
                           @NotNull LaunchStatus launchStatus); @NotNull RetryingInstallerResult install();  }
@Test public void singleInstallOnSuccess() { when(myInstaller.installApp(myDevice, myLaunchStatus)) .thenAnswer(invocationOnMock -> { myStopWatchTimeSource.advance(20); return new InstallResult(InstallResult.FailureCode.NO_ERROR, null, null); }); RetryingInstallerResult installResult = myRetryingInstaller.install(); assertThat(installResult.isSuccess()).isTrue(); assertThat(installResult.getRetryCount()).isEqualTo(1); assertThat(installResult.getTotalDuration().toMillis()).isEqualTo(20); assertThat(installResult.getLastInstallDuration().toMillis()).isEqualTo(20); verify(myInstaller, times(1)).installApp(myDevice, myLaunchStatus); verify(myPrompter, never()).showQuestionPrompt(anyString()); }
@Test public void notifyUserIfDeviceDisconnectedDuringInstall() { when (myInstaller.installApp(myDevice, myLaunchStatus)) .thenAnswer(invocation -> { myStopWatchTimeSource.advance(50); return new InstallResult(InstallResult.FailureCode.DEVICE_NOT_FOUND, null, null); }); when (myDevice.getName()) .thenReturn("Test Device"); RetryingInstallerResult installResult = myRetryingInstaller.install(); assertThat(installResult.isSuccess()).isFalse(); assertThat(installResult.getRetryCount()).isEqualTo(1); assertThat(installResult.getTotalDuration().toMillis()).isEqualTo(50); assertThat(installResult.getLastInstallDuration().toMillis()).isEqualTo(50); verify(myInstaller, times(1)).installApp(myDevice, myLaunchStatus); verify(myPrompter).showErrorMessage(AndroidBundle.message( "deployment.failed.reason.devicedisconnected", myDevice.getName())); }