[App banner] Add unit test for installing native app

Changes the basic showing test to show the banner and
simulate a user installing the app.

BUG=457414

Review URL: https://codereview.chromium.org/1137313005

Cr-Commit-Position: refs/heads/master@{#330229}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java
index c76db8c5..686fe8f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java
@@ -14,6 +14,7 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.banners.AppData;
 import org.chromium.chrome.browser.banners.InstallerDelegate;
@@ -25,6 +26,9 @@
  */
 @JNINamespace("banners")
 public class AppBannerInfoBarDelegate {
+    /** PackageManager to use in place of the real one. */
+    private static PackageManager sPackageManagerForTests;
+
     /** Weak pointer to the native AppBannerInfoBarDelegate. */
     private long mNativePointer;
 
@@ -34,6 +38,12 @@
     /** Monitors for application state changes. */
     private final ApplicationStatus.ApplicationStateListener mListener;
 
+    /** Overrides the PackageManager for testing. */
+    @VisibleForTesting
+    public static void setPackageManagerForTesting(PackageManager manager) {
+        sPackageManagerForTests = manager;
+    }
+
     private AppBannerInfoBarDelegate(long nativePtr) {
         mNativePointer = nativePtr;
         mListener = createApplicationStateListener();
@@ -64,7 +74,7 @@
     private boolean installOrOpenNativeApp(Tab tab, AppData appData) {
         Context context = ApplicationStatus.getApplicationContext();
         String packageName = appData.packageName();
-        PackageManager packageManager = context.getPackageManager();
+        PackageManager packageManager = getPackageManager(context);
 
         if (InstallerDelegate.isInstalled(packageManager, packageName)) {
             // Open the app.
@@ -89,7 +99,7 @@
                 if (isInstalling) {
                     // Start monitoring the install.
                     PackageManager pm =
-                            ApplicationStatus.getApplicationContext().getPackageManager();
+                            getPackageManager(ApplicationStatus.getApplicationContext());
                     mInstallTask = new InstallerDelegate(
                             Looper.getMainLooper(), pm, createInstallerDelegateObserver(),
                             appData.packageName());
@@ -121,12 +131,17 @@
     private int determineInstallState(AppData data) {
         if (mInstallTask != null) return AppBannerInfoBar.INSTALL_STATE_INSTALLING;
 
-        PackageManager pm = ApplicationStatus.getApplicationContext().getPackageManager();
+        PackageManager pm = getPackageManager(ApplicationStatus.getApplicationContext());
         boolean isInstalled = InstallerDelegate.isInstalled(pm, data.packageName());
         return isInstalled ? AppBannerInfoBar.INSTALL_STATE_INSTALLED
                 : AppBannerInfoBar.INSTALL_STATE_NOT_INSTALLED;
     }
 
+    private PackageManager getPackageManager(Context context) {
+        if (sPackageManagerForTests != null) return sPackageManagerForTests;
+        return context.getPackageManager();
+    }
+
     @CalledByNative
     private static AppBannerInfoBarDelegate create(long nativePtr) {
         return new AppBannerInfoBarDelegate(nativePtr);
diff --git a/chrome/android/javatests_shell/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/android/javatests_shell/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 538c34bd..6107c0b6 100644
--- a/chrome/android/javatests_shell/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/android/javatests_shell/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -4,11 +4,19 @@
 
 package org.chromium.chrome.browser.banners;
 
+import android.app.Activity;
+import android.app.Instrumentation.ActivityMonitor;
+import android.app.Instrumentation.ActivityResult;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.net.Uri;
+import android.test.mock.MockPackageManager;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.view.View;
+import android.widget.Button;
 import android.widget.TextView;
 
 import org.chromium.base.ThreadUtils;
@@ -18,6 +26,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.infobar.AnimationHelper;
 import org.chromium.chrome.browser.infobar.AppBannerInfoBar;
+import org.chromium.chrome.browser.infobar.AppBannerInfoBarDelegate;
 import org.chromium.chrome.browser.infobar.InfoBar;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.shell.ChromeShellTestBase;
@@ -28,6 +37,7 @@
 import org.chromium.content.browser.test.util.TouchCommon;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Tests the app banners.
@@ -42,24 +52,33 @@
 
     private static final String NATIVE_APP_TITLE = "Mock app title";
 
+    private static final String NATIVE_APP_PACKAGE = "123456";
+
+    private static final String NATIVE_APP_INSTALL_TEXT = "Install this";
+
     private static final String WEB_APP_URL =
             TestHttpServerClient.getUrl("chrome/test/data/banners/manifest_test_page.html");
 
     private static final String WEB_APP_TITLE = "Manifest test app";
 
+    private static final String INSTALL_ACTION = "INSTALL_ACTION";
+
     private static class MockAppDetailsDelegate extends AppDetailsDelegate {
         private Observer mObserver;
         private AppData mAppData;
         private int mNumRetrieved;
+        private Intent mInstallIntent;
 
         @Override
         protected void getAppDetailsAsynchronously(
                 Observer observer, String url, String packageName, int iconSize) {
             mNumRetrieved += 1;
             mObserver = observer;
+            mInstallIntent = new Intent(INSTALL_ACTION);
+
             mAppData = new AppData(url, packageName);
-            mAppData.setPackageInfo(
-                    NATIVE_APP_TITLE, NATIVE_ICON_URL, 4.5f, "Install this", null, null);
+            mAppData.setPackageInfo(NATIVE_APP_TITLE, NATIVE_ICON_URL, 4.5f,
+                    NATIVE_APP_INSTALL_TEXT, null, mInstallIntent);
             ThreadUtils.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
@@ -73,6 +92,23 @@
         }
     }
 
+    private static class TestPackageManager extends MockPackageManager {
+        public boolean isInstalled = false;
+
+        @Override
+        public List<PackageInfo> getInstalledPackages(int flags) {
+            List<PackageInfo> packages = new ArrayList<PackageInfo>();
+
+            if (isInstalled) {
+                PackageInfo info = new PackageInfo();
+                info.packageName = NATIVE_APP_PACKAGE;
+                packages.add(info);
+            }
+
+            return packages;
+        }
+    }
+
     private static class InfobarListener implements InfoBarContainer.InfoBarAnimationListener {
         private boolean mDoneAnimating;
 
@@ -83,12 +119,15 @@
     }
 
     private MockAppDetailsDelegate mDetailsDelegate;
+    private TestPackageManager mPackageManager;
 
     @Override
     protected void setUp() throws Exception {
         mDetailsDelegate = new MockAppDetailsDelegate();
+        mPackageManager = new TestPackageManager();
         AppBannerManager.setAppDetailsDelegate(mDetailsDelegate);
         AppBannerManager.setIsEnabledForTesting(true);
+        AppBannerInfoBarDelegate.setPackageManagerForTesting(mPackageManager);
         clearAppData();
 
         super.setUp();
@@ -139,19 +178,63 @@
 
     @SmallTest
     @Feature({"AppBanners"})
-    public void testBannerAppears() throws Exception {
+    public void testFullNativeInstallPathway() throws Exception {
         // Visit a site that requests a banner.
         assertTrue(CriteriaHelper.pollForUIThreadCriteria(
                 new TabLoadObserver(getActivity().getActiveTab(), NATIVE_APP_URL)));
         assertTrue(waitUntilAppDetailsRetrieved(1));
         assertTrue(waitUntilNoInfoBarsExist());
 
-        // Indicate a day has passed, then revisit the page.
+        // Indicate a day has passed, then revisit the page to get the banner to appear.
+        InfoBarContainer container = getActivity().getActiveTab().getInfoBarContainer();
+        final InfobarListener listener = new InfobarListener();
+        container.setAnimationListener(listener);
         AppBannerManager.setTimeDeltaForTesting(1);
         assertTrue(CriteriaHelper.pollForUIThreadCriteria(
                 new TabLoadObserver(getActivity().getActiveTab(), NATIVE_APP_URL)));
         assertTrue(waitUntilAppDetailsRetrieved(2));
         assertTrue(waitUntilAppBannerInfoBarAppears(NATIVE_APP_TITLE));
+        assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return listener.mDoneAnimating;
+            }
+        }));
+
+        // Check that the button asks if the user wants to install the app.
+        InfoBar infobar = container.getInfoBars().get(0);
+        final Button button =
+                (Button) infobar.getContentWrapper().findViewById(R.id.button_primary);
+        assertEquals(NATIVE_APP_INSTALL_TEXT, button.getText());
+
+        // Click the button to trigger the install.
+        final ActivityMonitor activityMonitor = new ActivityMonitor(
+                new IntentFilter(INSTALL_ACTION), new ActivityResult(Activity.RESULT_OK, null),
+                true);
+        getInstrumentation().addMonitor(activityMonitor);
+        TouchCommon.singleClickView(button);
+
+        // Wait for the infobar to register that the app is installing.
+        final String installingText =
+                getInstrumentation().getTargetContext().getString(R.string.app_banner_installing);
+        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return getInstrumentation().checkMonitorHit(activityMonitor, 1)
+                        && TextUtils.equals(button.getText(), installingText);
+            }
+        }));
+
+        // Say that the package is installed.  Infobar should say that the app is ready to open.
+        mPackageManager.isInstalled = true;
+        final String openText =
+                getInstrumentation().getTargetContext().getString(R.string.app_banner_open);
+        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return TextUtils.equals(button.getText(), openText);
+            }
+        }));
     }
 
     @MediumTest