chromium/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AmbientBadgeManagerTest.java

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.banners;

import android.app.Activity;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.app.Instrumentation.ActivityResult;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.text.TextUtils;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiSelector;

import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;

import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.HistogramWatcher;
import org.chromium.base.test.util.PackageManagerWrapper;
import org.chromium.chrome.browser.ShortcutHelper;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.browser.TabLoadObserver;
import org.chromium.chrome.test.util.browser.TabTitleObserver;
import org.chromium.chrome.test.util.browser.webapps.WebappTestPage;
import org.chromium.components.messages.DismissReason;
import org.chromium.components.messages.MessageBannerProperties;
import org.chromium.components.messages.MessageDispatcher;
import org.chromium.components.messages.MessageDispatcherProvider;
import org.chromium.components.messages.MessageIdentifier;
import org.chromium.components.messages.MessagesTestHelper;
import org.chromium.components.site_engagement.SiteEngagementService;
import org.chromium.components.webapps.AppBannerManager;
import org.chromium.components.webapps.AppData;
import org.chromium.components.webapps.AppDetailsDelegate;
import org.chromium.components.webapps.WebappInstallSource;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modaldialog.ModalDialogProperties.ButtonType;
import org.chromium.ui.modelutil.PropertyModel;

import java.util.Observer;

/** Tests the app banners. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class AmbientBadgeManagerTest {
    @Rule
    public ChromeTabbedActivityTestRule mTabbedActivityTestRule =
            new ChromeTabbedActivityTestRule();

    @Rule
    public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();

    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);

    // The ID of the last event received.
    private String mLastNotifyEvent;

    private static final String NATIVE_APP_MANIFEST_WITH_ID =
            "/chrome/test/data/banners/play_app_manifest.json";

    private static final String NATIVE_APP_MANIFEST_WITH_URL =
            "/chrome/test/data/banners/play_app_url_manifest.json";

    private static final String WEB_APP_MANIFEST_WITH_UNSUPPORTED_PLATFORM =
            "/chrome/test/data/banners/manifest_prefer_related_chrome_app.json";

    private static final String WEB_APP_MANIFEST_WITH_RELATED_APP_LIST =
            "/chrome/test/data/banners/manifest_listing_related_android_app.json";

    private static final String WEB_APP_MANIFEST_FOR_BOTTOM_SHEET_INSTALL =
            "/chrome/test/data/banners/manifest_with_screenshots.json";

    private static final String NATIVE_ICON_PATH = "/chrome/test/data/banners/launcher-icon-4x.png";

    private static final String NATIVE_APP_TITLE = "Mock app title";

    private static final String NATIVE_APP_INSTALL_TEXT = "Install this";

    private static final String NATIVE_APP_REFERRER = "chrome_inline&playinline=chrome_inline";

    private static final String NATIVE_APP_BLANK_REFERRER = "playinline=chrome_inline";

    private static final String NATIVE_APP_PACKAGE_NAME = "com.example.app";

    private static final String INSTALL_ACTION = "INSTALL_ACTION";

    private static final String INSTALL_PATH_HISTOGRAM_NAME = "WebApk.Install.PathToInstall";

    private static final String EXPECTED_DIALOG_TITLE = "Install app";

    private class MockAppDetailsDelegate extends AppDetailsDelegate {
        private Observer mObserver;
        private AppData mAppData;
        private int mNumRetrieved;
        private Intent mInstallIntent;
        private String mReferrer;

        @Override
        public void getAppDetailsAsynchronously(
                Observer observer, String url, String packageName, String referrer, int iconSize) {
            mNumRetrieved += 1;
            mObserver = observer;
            mReferrer = referrer;
            mInstallIntent = new Intent(INSTALL_ACTION);

            mAppData = new AppData(url, packageName);
            mAppData.setPackageInfo(
                    NATIVE_APP_TITLE,
                    mTestServer.getURL(NATIVE_ICON_PATH),
                    4.5f,
                    NATIVE_APP_INSTALL_TEXT,
                    null,
                    mInstallIntent);
            PostTask.runOrPostTask(
                    TaskTraits.UI_DEFAULT,
                    () -> {
                        mObserver.onAppDetailsRetrieved(mAppData);
                    });
        }

        @Override
        public void destroy() {}
    }

    private MockAppDetailsDelegate mDetailsDelegate;
    @Mock private PackageManager mPackageManager;
    private EmbeddedTestServer mTestServer;
    private UiDevice mUiDevice;

    @Before
    public void setUp() throws Exception {
        AppBannerManager.setIsSupported(true);
        ShortcutHelper.setDelegateForTests(
                new ShortcutHelper.Delegate() {
                    @Override
                    public void addShortcutToHomescreen(
                            String id,
                            String title,
                            Bitmap icon,
                            boolean iconAdaptive,
                            Intent shortcutIntent) {
                        // Ignore to prevent adding homescreen shortcuts.
                    }
                });

        mTabbedActivityTestRule.startMainActivityOnBlankPage();
        // Must be set after native has loaded.
        mDetailsDelegate = new MockAppDetailsDelegate();

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    AppBannerManager.setAppDetailsDelegate(mDetailsDelegate);
                });

        AppBannerManager.ignoreChromeChannelForTesting();
        AppBannerManager.setTotalEngagementForTesting(10);
        AppBannerManager.setOverrideSegmentationResultForTesting(true);
        mTestServer =
                EmbeddedTestServer.createAndStartServer(
                        ApplicationProvider.getApplicationContext());
        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    }

    private void resetEngagementForUrl(final String url, final double engagement) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    // TODO (https://crbug.com/1063807):  Add incognito mode tests.
                    SiteEngagementService.getForBrowserContext(
                                    ProfileManager.getLastUsedRegularProfile())
                            .resetBaseScoreForUrl(url, engagement);
                });
    }

    private AppBannerManager getAppBannerManager(WebContents webContents) {
        return AppBannerManager.forWebContents(webContents);
    }

    private void waitForBannerManager(Tab tab) {
        CriteriaHelper.pollUiThread(
                () -> !getAppBannerManager(tab.getWebContents()).isRunningForTesting());
    }

    private void waitForAppBannerPipelineStatus(Tab tab, int expectedValue) {
        CriteriaHelper.pollUiThread(
                () -> {
                    return getAppBannerManager(tab.getWebContents()).getPipelineStatusForTesting()
                            == expectedValue;
                });
    }

    private void assertAppBannerPipelineStatus(int expectedValue) {
        Tab tab = mTabbedActivityTestRule.getActivity().getActivityTab();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    Assert.assertEquals(
                            expectedValue,
                            getAppBannerManager(tab.getWebContents())
                                    .getPipelineStatusForTesting());
                });
    }

    private void navigateToUrlAndWaitForBannerManager(
            ChromeActivityTestRule<? extends ChromeActivity> rule, String url) throws Exception {
        Tab tab = rule.getActivity().getActivityTab();
        new TabLoadObserver(tab).fullyLoadUrl(url);
        waitForBannerManager(tab);
    }

    private void waitUntilAppDetailsRetrieved(
            ChromeActivityTestRule<? extends ChromeActivity> rule, final int numExpected) {
        CriteriaHelper.pollUiThread(
                () -> {
                    AppBannerManager manager =
                            getAppBannerManager(
                                    rule.getActivity().getActivityTab().getWebContents());
                    Criteria.checkThat(mDetailsDelegate.mNumRetrieved, Matchers.is(numExpected));
                    Criteria.checkThat(manager.isRunningForTesting(), Matchers.is(false));
                });
    }

    private void waitUntilAmbientBadgePromptAppears(
            ChromeActivityTestRule<? extends ChromeActivity> rule) {
        WindowAndroid windowAndroid = rule.getActivity().getWindowAndroid();
        CriteriaHelper.pollUiThread(
                () -> {
                    Criteria.checkThat(
                            MessagesTestHelper.getMessageCount(windowAndroid), Matchers.is(1));
                    Criteria.checkThat(
                            MessagesTestHelper.getMessageIdentifier(windowAndroid, 0),
                            Matchers.is(MessageIdentifier.INSTALLABLE_AMBIENT_BADGE));
                });
    }

    private void checkAmbientBadgePromptNotExist(
            ChromeActivityTestRule<? extends ChromeActivity> rule) {
        WindowAndroid windowAndroid = rule.getActivity().getWindowAndroid();
        ThreadUtils.runOnUiThreadBlocking(
                () -> Assert.assertEquals(0, MessagesTestHelper.getMessageCount(windowAndroid)));
    }

    private void waitForBadgeStatus(Tab tab, int expectedValue) {
        CriteriaHelper.pollUiThread(
                () -> {
                    return getAppBannerManager(tab.getWebContents()).getBadgeStatusForTesting()
                            == expectedValue;
                });
    }

    private void waitForModalBanner(final ChromeActivity activity) throws Exception {
        UiObject dialogUiObject =
                mUiDevice.findObject(new UiSelector().text(EXPECTED_DIALOG_TITLE));
        Assert.assertTrue(dialogUiObject.waitForExists(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL));
    }

    private void triggerInstallWebApp(
            ChromeActivityTestRule<? extends ChromeActivity> rule, String url) throws Exception {
        resetEngagementForUrl(url, 10);
        rule.loadUrlInNewTab(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
        navigateToUrlAndWaitForBannerManager(rule, url);
        waitUntilAmbientBadgePromptAppears(rule);

        dismissAmbientBadgeMessage(rule, true);
        waitForModalBanner(rule.getActivity());

        // Click the button to trigger the adding of the shortcut.
        clickButton(rule.getActivity(), ButtonType.POSITIVE);
    }

    private void triggerInstallNative(
            ChromeActivityTestRule<? extends ChromeActivity> rule,
            String url,
            String expectedReferrer)
            throws Exception {
        resetEngagementForUrl(url, 10);
        rule.loadUrlInNewTab(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
        navigateToUrlAndWaitForBannerManager(rule, url);
        waitUntilAppDetailsRetrieved(rule, 1);
        waitUntilAmbientBadgePromptAppears(rule);
        Assert.assertEquals(mDetailsDelegate.mReferrer, expectedReferrer);

        dismissAmbientBadgeMessage(rule, true);
        final ChromeActivity activity = rule.getActivity();
        waitForModalBanner(activity);

        // Click the button to trigger the installation.
        final ActivityMonitor activityMonitor =
                new ActivityMonitor(
                        new IntentFilter(INSTALL_ACTION),
                        new ActivityResult(Activity.RESULT_OK, null),
                        true);
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        instrumentation.addMonitor(activityMonitor);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    String buttonText =
                            activity.getModalDialogManager()
                                    .getCurrentDialogForTest()
                                    .get(ModalDialogProperties.POSITIVE_BUTTON_TEXT);
                    Assert.assertEquals(NATIVE_APP_INSTALL_TEXT, buttonText);
                });

        clickButton(activity, ButtonType.POSITIVE);

        // Wait until the installation triggers.
        instrumentation.waitForMonitorWithTimeout(
                activityMonitor, CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL);
    }

    private void clickButton(final ChromeActivity activity, @ButtonType final int buttonType) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    PropertyModel model =
                            activity.getModalDialogManager().getCurrentDialogForTest();
                    model.get(ModalDialogProperties.CONTROLLER).onClick(model, buttonType);
                });
    }

    private void dismissAmbientBadgeMessage(
            ChromeActivityTestRule<? extends ChromeActivity> rule, boolean accept)
            throws Exception {
        WindowAndroid windowAndroid = rule.getActivity().getWindowAndroid();

        MessageDispatcher dispatcher =
                ThreadUtils.runOnUiThreadBlocking(
                        () -> MessageDispatcherProvider.from(windowAndroid));
        PropertyModel model =
                ThreadUtils.runOnUiThreadBlocking(
                        () ->
                                MessagesTestHelper.getCurrentMessage(
                                        MessagesTestHelper.getEnqueuedMessages(
                                                        dispatcher,
                                                        MessageIdentifier.INSTALLABLE_AMBIENT_BADGE)
                                                .get(0)));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    if (accept) {
                        model.get(MessageBannerProperties.ON_PRIMARY_ACTION).get();
                    } else {
                        dispatcher.dismissMessage(model, DismissReason.GESTURE);
                    }
                });
    }

    @Test
    @SmallTest
    public void testAmbientBadgeInstalledWebAppBrowserTab() throws Exception {
        HistogramWatcher watcher =
                HistogramWatcher.newBuilder()
                        .expectIntRecord(
                                "Webapp.Install.InstallEvent",
                                WebappInstallSource.AMBIENT_BADGE_BROWSER_TAB)
                        .expectIntRecord(INSTALL_PATH_HISTOGRAM_NAME, 1 /* kAmbientInfobar */)
                        .build();

        triggerInstallWebApp(
                mTabbedActivityTestRule,
                WebappTestPage.getNonServiceWorkerUrlWithAction(
                        mTestServer, "verify_appinstalled"));

        // The appinstalled event should fire (and cause the title to change).
        new TabTitleObserver(
                        mTabbedActivityTestRule.getActivity().getActivityTab(),
                        "Got appinstalled: listener, attr")
                .waitForTitleUpdate(3);

        watcher.assertExpected();
    }

    @Test
    @SmallTest
    public void testAmbientBadgeInstalledWebAppCustomTab() throws Exception {
        HistogramWatcher watcher =
                HistogramWatcher.newBuilder()
                        .expectIntRecord(
                                "Webapp.Install.InstallEvent",
                                WebappInstallSource.AMBIENT_BADGE_CUSTOM_TAB)
                        .expectIntRecord(INSTALL_PATH_HISTOGRAM_NAME, 1 /* kAmbientInfobar */)
                        .build();

        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(
                CustomTabsIntentTestUtils.createMinimalCustomTabIntent(
                        ApplicationProvider.getApplicationContext(),
                        ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL));
        triggerInstallWebApp(
                mCustomTabActivityTestRule,
                WebappTestPage.getNonServiceWorkerUrlWithAction(
                        mTestServer, "verify_appinstalled"));

        // The appinstalled event should fire (and cause the title to change).
        new TabTitleObserver(
                        mCustomTabActivityTestRule.getActivity().getActivityTab(),
                        "Got appinstalled: listener, attr")
                .waitForTitleUpdate(3);

        watcher.assertExpected();
    }

    @Test
    @SmallTest
    public void testAmbientBadgeInstalledNativeAppBrowserTab() throws Exception {
        HistogramWatcher watcher =
                HistogramWatcher.newBuilder().expectNoRecords(INSTALL_PATH_HISTOGRAM_NAME).build();

        triggerInstallNative(
                mTabbedActivityTestRule,
                WebappTestPage.getNonServiceWorkerUrlWithManifest(
                        mTestServer, NATIVE_APP_MANIFEST_WITH_ID),
                NATIVE_APP_BLANK_REFERRER);

        watcher.assertExpected();
    }

    @Test
    @SmallTest
    public void testAmbientBadgeInstalledNativeAppCustomTab() throws Exception {
        HistogramWatcher watcher =
                HistogramWatcher.newBuilder().expectNoRecords(INSTALL_PATH_HISTOGRAM_NAME).build();

        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(
                CustomTabsIntentTestUtils.createMinimalCustomTabIntent(
                        ApplicationProvider.getApplicationContext(),
                        ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL));

        triggerInstallNative(
                mCustomTabActivityTestRule,
                WebappTestPage.getNonServiceWorkerUrlWithManifest(
                        mTestServer, NATIVE_APP_MANIFEST_WITH_ID),
                NATIVE_APP_BLANK_REFERRER);

        watcher.assertExpected();
    }

    @Test
    @MediumTest
    @CommandLineFlags.Add({"bypass-installable-message-throttle-for-testing"})
    public void testBlockedAmbientBadgeDoesNotAppearAgainForMonths() throws Exception {
        HistogramWatcher watcher =
                HistogramWatcher.newBuilder().expectNoRecords(INSTALL_PATH_HISTOGRAM_NAME).build();

        // Visit a site that is a PWA. The ambient badge should show.
        String webBannerUrl = WebappTestPage.getNonServiceWorkerUrl(mTestServer);
        resetEngagementForUrl(webBannerUrl, 10);
        Tab tab = mTabbedActivityTestRule.getActivity().getActivityTab();
        new TabLoadObserver(tab).fullyLoadUrl(webBannerUrl);
        waitUntilAmbientBadgePromptAppears(mTabbedActivityTestRule);

        WindowAndroid windowAndroid = mTabbedActivityTestRule.getActivity().getWindowAndroid();

        // Explicitly dismiss the ambient badge.
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                MessagesTestHelper.getMessageCount(windowAndroid), Matchers.is(1)));

        MessageDispatcher dispatcher =
                ThreadUtils.runOnUiThreadBlocking(
                        () -> MessageDispatcherProvider.from(windowAndroid));
        PropertyModel model =
                ThreadUtils.runOnUiThreadBlocking(
                        () ->
                                MessagesTestHelper.getCurrentMessage(
                                        MessagesTestHelper.getEnqueuedMessages(
                                                        dispatcher,
                                                        MessageIdentifier.INSTALLABLE_AMBIENT_BADGE)
                                                .get(0)));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    dispatcher.dismissMessage(model, DismissReason.GESTURE);
                });
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                MessagesTestHelper.getMessageCount(windowAndroid), Matchers.is(0)));

        // Waiting two months shouldn't be long enough.
        AppBannerManager.setTimeDeltaForTesting(61);
        new TabLoadObserver(tab).fullyLoadUrl(webBannerUrl);
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                MessagesTestHelper.getMessageCount(windowAndroid), Matchers.is(0)));

        AppBannerManager.setTimeDeltaForTesting(62);
        new TabLoadObserver(tab).fullyLoadUrl(webBannerUrl);
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                MessagesTestHelper.getMessageCount(windowAndroid), Matchers.is(0)));

        // Waiting three months should allow the ambient badge to reappear.
        AppBannerManager.setTimeDeltaForTesting(91);
        new TabLoadObserver(tab).fullyLoadUrl(webBannerUrl);
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                MessagesTestHelper.getMessageCount(windowAndroid), Matchers.is(1)));

        watcher.assertExpected();
    }

    @Test
    @MediumTest
    public void testAmbientBadgeAppearWithServiceWorkerPage() throws Exception {
        String webBannerUrl = WebappTestPage.getNonServiceWorkerUrl(mTestServer);
        resetEngagementForUrl(webBannerUrl, 10);
        navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, webBannerUrl);

        Tab tab = mTabbedActivityTestRule.getActivity().getActivityTab();
        waitForBadgeStatus(tab, AmbientBadgeState.SHOWING);
        waitUntilAmbientBadgePromptAppears(mTabbedActivityTestRule);
    }

    @Test
    @SmallTest
    public void testAmbientBadgeTriggeredWithListedRelatedApp() throws Exception {
        // The ambient badge should show if there is play app in related applications list but
        // preferred_related_applications is false.
        String webBannerUrl = WebappTestPage.getNonServiceWorkerUrl(mTestServer);
        resetEngagementForUrl(webBannerUrl, 10);
        navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, webBannerUrl);

        waitUntilAmbientBadgePromptAppears(mTabbedActivityTestRule);
    }

    private static class TestContext extends ContextWrapper {
        public TestContext(Context baseContext) {
            super(baseContext);
        }

        @Override
        public PackageManager getPackageManager() {
            return new PackageManagerWrapper(super.getPackageManager()) {
                @Override
                public PackageInfo getPackageInfo(String packageName, int flags)
                        throws PackageManager.NameNotFoundException {
                    if (TextUtils.equals(NATIVE_APP_PACKAGE_NAME, packageName)) {
                        PackageInfo packageInfo = new PackageInfo();
                        packageInfo.packageName = NATIVE_APP_PACKAGE_NAME;
                        return packageInfo;
                    }

                    return TestContext.super.getPackageManager().getPackageInfo(packageName, flags);
                }
            };
        }
    }

    @Test
    @SmallTest
    public void testAmbientBadgeDoesNotAppearWhenRelatedAppInstalled() throws Exception {
        String url =
                WebappTestPage.getNonServiceWorkerUrlWithManifest(
                        mTestServer, WEB_APP_MANIFEST_WITH_RELATED_APP_LIST);
        resetEngagementForUrl(url, 10);

        final Context contextToRestore = ContextUtils.getApplicationContext();
        ContextUtils.initApplicationContextForTests(new TestContext(contextToRestore));

        navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, url);

        assertAppBannerPipelineStatus(AppBannerManagerState.COMPLETE);

        // The web app banner not show if a play app in related applications list is installed.
        checkAmbientBadgePromptNotExist(mTabbedActivityTestRule);

        ContextUtils.initApplicationContextForTests(contextToRestore);
    }

    @Test
    @SmallTest
    public void testMlShowAmbientBadge() throws Exception {
        String url = WebappTestPage.getNonServiceWorkerUrl(mTestServer);
        resetEngagementForUrl(url, 10);
        AppBannerManager.setOverrideSegmentationResultForTesting(false);

        navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, url);

        assertAppBannerPipelineStatus(AppBannerManagerState.PENDING_PROMPT_NOT_CANCELED);

        Tab tab = mTabbedActivityTestRule.getActivity().getActivityTab();

        // Blocked by segmentation result.
        waitForBadgeStatus(tab, AmbientBadgeState.SEGMENTATION_BLOCK);
        checkAmbientBadgePromptNotExist(mTabbedActivityTestRule);

        // Advance 3 days and navigate to |url| again
        AppBannerManager.setTimeDeltaForTesting(3);
        AppBannerManager.setOverrideSegmentationResultForTesting(true);
        mTabbedActivityTestRule.loadUrl(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
        navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, url);

        waitForBadgeStatus(tab, AmbientBadgeState.SHOWING);
        waitUntilAmbientBadgePromptAppears(mTabbedActivityTestRule);
    }
}