|
31 | 31 | import static com.google.firebase.appdistribution.impl.FeedbackActivity.RELEASE_NAME_EXTRA_KEY;
|
32 | 32 | import static com.google.firebase.appdistribution.impl.FeedbackActivity.SCREENSHOT_URI_EXTRA_KEY;
|
33 | 33 | import static com.google.firebase.appdistribution.impl.TestUtils.assertTaskFailure;
|
| 34 | +import static java.util.stream.Collectors.toList; |
34 | 35 | import static org.junit.Assert.assertEquals;
|
35 | 36 | import static org.junit.Assert.assertFalse;
|
36 | 37 | import static org.junit.Assert.assertNotNull;
|
37 | 38 | import static org.junit.Assert.assertNull;
|
38 | 39 | import static org.junit.Assert.assertTrue;
|
| 40 | +import static org.mockito.ArgumentMatchers.any; |
39 | 41 | import static org.mockito.Mockito.doReturn;
|
40 | 42 | import static org.mockito.Mockito.never;
|
41 | 43 | import static org.mockito.Mockito.spy;
|
|
53 | 55 | import android.content.pm.PackageInfo;
|
54 | 56 | import android.net.Uri;
|
55 | 57 | import android.os.Bundle;
|
| 58 | +import android.util.Log; |
56 | 59 | import androidx.test.core.app.ApplicationProvider;
|
57 | 60 | import androidx.test.core.content.pm.ApplicationInfoBuilder;
|
58 | 61 | import androidx.test.core.content.pm.PackageInfoBuilder;
|
|
74 | 77 | import java.util.concurrent.ExecutorService;
|
75 | 78 | import java.util.concurrent.Executors;
|
76 | 79 | import java.util.concurrent.Future;
|
| 80 | +import java.util.function.Predicate; |
| 81 | +import org.junit.After; |
77 | 82 | import org.junit.Before;
|
78 | 83 | import org.junit.Test;
|
79 | 84 | import org.junit.runner.RunWith;
|
80 |
| -import org.mockito.ArgumentCaptor; |
81 | 85 | import org.mockito.Mock;
|
82 | 86 | import org.mockito.MockitoAnnotations;
|
83 | 87 | import org.robolectric.Robolectric;
|
84 | 88 | import org.robolectric.RobolectricTestRunner;
|
| 89 | +import org.robolectric.RuntimeEnvironment; |
| 90 | +import org.robolectric.android.controller.ActivityController; |
85 | 91 | import org.robolectric.shadows.ShadowAlertDialog;
|
| 92 | +import org.robolectric.shadows.ShadowLog; |
86 | 93 | import org.robolectric.shadows.ShadowPackageManager;
|
87 | 94 |
|
88 | 95 | @RunWith(RobolectricTestRunner.class)
|
@@ -122,6 +129,7 @@ public class FirebaseAppDistributionServiceImplTest {
|
122 | 129 | .setDownloadUrl(TEST_URL);
|
123 | 130 |
|
124 | 131 | private FirebaseAppDistributionImpl firebaseAppDistribution;
|
| 132 | + private ActivityController<TestActivity> activityController; |
125 | 133 | private TestActivity activity;
|
126 | 134 | private FirebaseApp firebaseApp;
|
127 | 135 | private ExecutorService taskExecutor = Executors.newSingleThreadExecutor();
|
@@ -190,11 +198,19 @@ public void setup() throws FirebaseAppDistributionException {
|
190 | 198 | packageInfo.setLongVersionCode(INSTALLED_VERSION_CODE);
|
191 | 199 | shadowPackageManager.installPackage(packageInfo);
|
192 | 200 |
|
193 |
| - activity = spy(Robolectric.buildActivity(TestActivity.class).create().get()); |
| 201 | + activityController = Robolectric.buildActivity(TestActivity.class).setup(); |
| 202 | + activity = spy(activityController.get()); |
194 | 203 | TestUtils.mockForegroundActivity(mockLifecycleNotifier, activity);
|
195 | 204 | when(mockScreenshotTaker.takeScreenshot()).thenReturn(Tasks.forResult(TEST_SCREENSHOT_URI));
|
196 | 205 | }
|
197 | 206 |
|
| 207 | + @After |
| 208 | + public void tearDown() { |
| 209 | + if (activityController != null) { |
| 210 | + activityController.close(); |
| 211 | + } |
| 212 | + } |
| 213 | + |
198 | 214 | @Test
|
199 | 215 | public void checkForNewRelease_whenCheckForNewReleaseFails_throwsError() {
|
200 | 216 | when(mockNewReleaseFetcher.checkForNewRelease())
|
@@ -635,37 +651,104 @@ public void updateApp_withApkReleaseAvailable_returnsSameApkTask() {
|
635 | 651 | @Test
|
636 | 652 | public void startFeedback_signsInTesterAndStartsActivity() throws InterruptedException {
|
637 | 653 | when(mockReleaseIdentifier.identifyRelease()).thenReturn(Tasks.forResult("release-name"));
|
| 654 | + |
638 | 655 | firebaseAppDistribution.startFeedback("Some terms and conditions");
|
639 | 656 | TestUtils.awaitAsyncOperations(taskExecutor);
|
640 | 657 |
|
641 |
| - ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class); |
642 |
| - verify(activity).startActivity(argument.capture()); |
643 | 658 | verify(mockTesterSignInManager).signInTester();
|
644 |
| - assertThat(argument.getValue().getStringExtra(RELEASE_NAME_EXTRA_KEY)) |
645 |
| - .isEqualTo("release-name"); |
646 |
| - assertThat(argument.getValue().getStringExtra(SCREENSHOT_URI_EXTRA_KEY)) |
| 659 | + Intent expectedIntent = new Intent(activity, FeedbackActivity.class); |
| 660 | + Intent actualIntent = shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity(); |
| 661 | + assertEquals(expectedIntent.getComponent(), actualIntent.getComponent()); |
| 662 | + assertThat(actualIntent.getStringExtra(RELEASE_NAME_EXTRA_KEY)).isEqualTo("release-name"); |
| 663 | + assertThat(actualIntent.getStringExtra(SCREENSHOT_URI_EXTRA_KEY)) |
647 | 664 | .isEqualTo(TEST_SCREENSHOT_URI.toString());
|
648 |
| - assertThat(argument.getValue().getStringExtra(INFO_TEXT_EXTRA_KEY)) |
| 665 | + assertThat(actualIntent.getStringExtra(INFO_TEXT_EXTRA_KEY)) |
649 | 666 | .isEqualTo("Some terms and conditions");
|
| 667 | + assertThat(firebaseAppDistribution.isFeedbackInProgress()).isTrue(); |
| 668 | + } |
| 669 | + |
| 670 | + @Test |
| 671 | + public void startFeedback_calledMultipleTimes_onlyStartsOnce() throws InterruptedException { |
| 672 | + when(mockReleaseIdentifier.identifyRelease()).thenReturn(Tasks.forResult("release-name")); |
| 673 | + |
| 674 | + firebaseAppDistribution.startFeedback("Some terms and conditions"); |
| 675 | + firebaseAppDistribution.startFeedback("Some other terms and conditions"); |
| 676 | + TestUtils.awaitAsyncOperations(taskExecutor); |
| 677 | + |
| 678 | + verify(activity, times(1)).startActivity(any()); |
| 679 | + } |
| 680 | + |
| 681 | + @Test |
| 682 | + public void startFeedback_closingActivity_setsInProgressToFalse() throws InterruptedException { |
| 683 | + when(mockReleaseIdentifier.identifyRelease()).thenReturn(Tasks.forResult("release-name")); |
| 684 | + |
| 685 | + firebaseAppDistribution.startFeedback("Some terms and conditions"); |
| 686 | + TestUtils.awaitAsyncOperations(taskExecutor); |
| 687 | + // Simulate destroying the feedback activity |
| 688 | + firebaseAppDistribution.onActivityDestroyed(new FeedbackActivity()); |
| 689 | + |
| 690 | + assertThat(firebaseAppDistribution.isFeedbackInProgress()).isFalse(); |
650 | 691 | }
|
651 | 692 |
|
652 | 693 | @Test
|
653 | 694 | public void startFeedback_screenshotFails_startActivityWithNoScreenshot()
|
654 |
| - throws InterruptedException { |
| 695 | + throws InterruptedException { |
655 | 696 | when(mockScreenshotTaker.takeScreenshot())
|
656 |
| - .thenReturn( |
657 |
| - Tasks.forException(new FirebaseAppDistributionException("Error", Status.UNKNOWN))); |
| 697 | + .thenReturn( |
| 698 | + Tasks.forException(new FirebaseAppDistributionException("Error", Status.UNKNOWN))); |
658 | 699 | when(mockReleaseIdentifier.identifyRelease()).thenReturn(Tasks.forResult("release-name"));
|
| 700 | + |
659 | 701 | firebaseAppDistribution.startFeedback("Some terms and conditions");
|
660 | 702 | TestUtils.awaitAsyncOperations(taskExecutor);
|
661 | 703 |
|
662 |
| - ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class); |
663 |
| - verify(activity).startActivity(argument.capture()); |
664 | 704 | verify(mockTesterSignInManager).signInTester();
|
665 |
| - assertThat(argument.getValue().getStringExtra(RELEASE_NAME_EXTRA_KEY)) |
666 |
| - .isEqualTo("release-name"); |
667 |
| - assertThat(argument.getValue().hasExtra(SCREENSHOT_URI_EXTRA_KEY)).isFalse(); |
668 |
| - assertThat(argument.getValue().getStringExtra(INFO_TEXT_EXTRA_KEY)) |
669 |
| - .isEqualTo("Some terms and conditions"); |
| 705 | + Intent expectedIntent = new Intent(activity, FeedbackActivity.class); |
| 706 | + Intent actualIntent = shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity(); |
| 707 | + assertEquals(expectedIntent.getComponent(), actualIntent.getComponent()); |
| 708 | + assertThat(actualIntent.getStringExtra(RELEASE_NAME_EXTRA_KEY)).isEqualTo("release-name"); |
| 709 | + assertThat(actualIntent.getStringExtra(SCREENSHOT_URI_EXTRA_KEY)).isNull(); |
| 710 | + assertThat(actualIntent.getStringExtra(INFO_TEXT_EXTRA_KEY)) |
| 711 | + .isEqualTo("Some terms and conditions"); |
| 712 | + assertThat(firebaseAppDistribution.isFeedbackInProgress()).isTrue(); |
| 713 | + } |
| 714 | + |
| 715 | + @Test |
| 716 | + public void startFeedback_signInTesterFails_logsAndSetsInProgressToFalse() |
| 717 | + throws InterruptedException { |
| 718 | + when(mockReleaseIdentifier.identifyRelease()).thenReturn(Tasks.forResult("release-name")); |
| 719 | + FirebaseAppDistributionException exception = |
| 720 | + new FirebaseAppDistributionException("Error", Status.UNKNOWN); |
| 721 | + when(mockTesterSignInManager.signInTester()).thenReturn(Tasks.forException(exception)); |
| 722 | + |
| 723 | + firebaseAppDistribution.startFeedback("Some terms and conditions"); |
| 724 | + TestUtils.awaitAsyncOperations(taskExecutor); |
| 725 | + |
| 726 | + assertThat(firebaseAppDistribution.isFeedbackInProgress()).isFalse(); |
| 727 | + assertLoggedError("Failed to launch feedback flow", exception); |
| 728 | + } |
| 729 | + |
| 730 | + @Test |
| 731 | + public void startFeedback_cantIdentifyRelease_logsAndSetsInProgressToFalse() |
| 732 | + throws InterruptedException { |
| 733 | + FirebaseAppDistributionException exception = |
| 734 | + new FirebaseAppDistributionException("Error", Status.UNKNOWN); |
| 735 | + when(mockReleaseIdentifier.identifyRelease()).thenReturn(Tasks.forException(exception)); |
| 736 | + |
| 737 | + firebaseAppDistribution.startFeedback("Some terms and conditions"); |
| 738 | + TestUtils.awaitAsyncOperations(taskExecutor); |
| 739 | + |
| 740 | + assertThat(firebaseAppDistribution.isFeedbackInProgress()).isFalse(); |
| 741 | + assertLoggedError("Failed to launch feedback flow", exception); |
| 742 | + } |
| 743 | + |
| 744 | + private static void assertLoggedError(String partialMessage, Throwable e) { |
| 745 | + Predicate<ShadowLog.LogItem> predicate = |
| 746 | + log -> |
| 747 | + log.type == Log.ERROR |
| 748 | + && log.msg.contains(partialMessage) |
| 749 | + && (e != null ? log.throwable == e : true); |
| 750 | + List<ShadowLog.LogItem> matchingLogs = |
| 751 | + ShadowLog.getLogs().stream().filter(predicate).collect(toList()); |
| 752 | + assertThat(matchingLogs).hasSize(1); |
670 | 753 | }
|
671 | 754 | }
|
0 commit comments