chromium/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorTest.java

// Copyright 2015 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.tabmodel;

import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_LOW_END_DEVICE;
import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;

import android.content.Intent;

import androidx.test.filters.MediumTest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.chrome.test.util.ChromeTabUtils;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.net.test.EmbeddedTestServerRule;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

/** Tests for ChromeTabCreator. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@Batch(Batch.PER_CLASS)
public class ChromeTabCreatorTest {
    @ClassRule
    public static ChromeTabbedActivityTestRule sActivityTestRule =
            new ChromeTabbedActivityTestRule();

    @Rule
    public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule =
            new BlankCTATabInitialStateRule(sActivityTestRule, false);

    @ClassRule public static EmbeddedTestServerRule sTestServerRule = new EmbeddedTestServerRule();

    private static final String TEST_PATH = "/chrome/test/data/android/about.html";

    private EmbeddedTestServer mTestServer;

    @Before
    public void setUp() throws Exception {
        mTestServer = sTestServerRule.getServer();
    }

    /** Verify that tabs opened in background on low-end are loaded lazily. */
    @Test
    @Restriction(RESTRICTION_TYPE_LOW_END_DEVICE)
    @MediumTest
    @Feature({"Browser"})
    public void testCreateNewTabInBackgroundLowEnd() throws ExecutionException {
        final Tab fgTab = sActivityTestRule.getActivity().getActivityTab();
        final Tab bgTab =
                ThreadUtils.runOnUiThreadBlocking(
                        new Callable<Tab>() {
                            @Override
                            public Tab call() {
                                return sActivityTestRule
                                        .getActivity()
                                        .getCurrentTabCreator()
                                        .createNewTab(
                                                new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
                                                TabLaunchType.FROM_LONGPRESS_BACKGROUND,
                                                fgTab);
                            }
                        });

        // Verify that the background tab is not loading.
        Assert.assertFalse(bgTab.isLoading());

        // Switch tabs and verify that the tab is loaded as it gets foregrounded.
        ChromeTabUtils.waitForTabPageLoaded(
                bgTab,
                mTestServer.getURL(TEST_PATH),
                new Runnable() {
                    @Override
                    public void run() {
                        ThreadUtils.runOnUiThreadBlocking(
                                () -> {
                                    TabModelUtils.setIndex(
                                            sActivityTestRule.getActivity().getCurrentTabModel(),
                                            indexOf(bgTab));
                                });
                    }
                });
        Assert.assertNotNull(bgTab.getView());
    }

    /** Verify that tabs opened in background on regular devices are loaded eagerly. */
    @Test
    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
    @MediumTest
    @Feature({"Browser"})
    public void testCreateNewTabInBackground() throws ExecutionException {
        final Tab fgTab = sActivityTestRule.getActivity().getActivityTab();
        Tab bgTab =
                ThreadUtils.runOnUiThreadBlocking(
                        new Callable<Tab>() {
                            @Override
                            public Tab call() {
                                return sActivityTestRule
                                        .getActivity()
                                        .getCurrentTabCreator()
                                        .createNewTab(
                                                new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
                                                TabLaunchType.FROM_LONGPRESS_BACKGROUND,
                                                fgTab);
                            }
                        });

        // Verify that the background tab is loaded.
        Assert.assertNotNull(bgTab.getView());
        ChromeTabUtils.waitForTabPageLoaded(bgTab, mTestServer.getURL(TEST_PATH));

        // Both foreground and background do not request desktop sites.
        Assert.assertFalse(
                "Should not request desktop sites by default.",
                fgTab.getWebContents().getNavigationController().getUseDesktopUserAgent());
        Assert.assertFalse(
                "Should not request desktop sites by default.",
                bgTab.getWebContents().getNavigationController().getUseDesktopUserAgent());
    }

    /** Verify that the spare WebContents is used. */
    @Test
    @MediumTest
    @Feature({"Browser"})
    public void testCreateNewTabTakesSpareWebContents() throws Throwable {
        ThreadUtils.runOnUiThreadBlocking(
                new Runnable() {
                    @Override
                    public void run() {
                        Tab currentTab = sActivityTestRule.getActivity().getActivityTab();
                        WarmupManager.getInstance()
                                .createSpareWebContents(sActivityTestRule.getProfile(false));
                        Assert.assertTrue(WarmupManager.getInstance().hasSpareWebContents());
                        sActivityTestRule
                                .getActivity()
                                .getCurrentTabCreator()
                                .createNewTab(
                                        new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
                                        TabLaunchType.FROM_EXTERNAL_APP,
                                        currentTab);
                        Assert.assertFalse(WarmupManager.getInstance().hasSpareWebContents());
                    }
                });
    }

    /** Verify that the tab position is set using the intent. */
    @Test
    @MediumTest
    @Feature({"Browser"})
    public void testCreateNewTabTakesPositonIndex() throws Throwable {
        ThreadUtils.runOnUiThreadBlocking(
                new Runnable() {
                    @Override
                    public void run() {
                        Tab currentTab = sActivityTestRule.getActivity().getActivityTab();
                        Tab tabOne =
                                sActivityTestRule
                                        .getActivity()
                                        .getCurrentTabCreator()
                                        .createNewTab(
                                                new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
                                                TabLaunchType.FROM_EXTERNAL_APP,
                                                currentTab);
                        Tab tabTwo =
                                sActivityTestRule
                                        .getActivity()
                                        .getCurrentTabCreator()
                                        .createNewTab(
                                                new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
                                                TabLaunchType.FROM_LINK,
                                                null,
                                                createIntent(/* tabIndex= */ 0)); // At the start.
                        Assert.assertFalse(
                                "The second/last tab should be the first in the list.",
                                0 == indexOf(tabTwo));
                        Assert.assertFalse(
                                "The current tab should now be the second in the list.",
                                1 == indexOf(currentTab));
                        Assert.assertFalse(
                                "The first tab should now be the third in the list.",
                                2 == indexOf(tabOne));
                    }
                });
    }

    /** Verify that tabs opened in background when launch type is FROM_SYNC_BACKGROUND. */
    @Test
    @MediumTest
    @Feature({"Browser"})
    public void testCreateNewTabWithSyncBackgroundFrozen() throws ExecutionException {
        final String url = mTestServer.getURL(TEST_PATH);
        final String title = "BAR";
        final Tab bgTab =
                ThreadUtils.runOnUiThreadBlocking(
                        () -> {
                            Tab tab =
                                    sActivityTestRule
                                            .getActivity()
                                            .getCurrentTabCreator()
                                            .createNewTab(
                                                    new LoadUrlParams(url),
                                                    title,
                                                    TabLaunchType.FROM_SYNC_BACKGROUND,
                                                    null,
                                                    TabModel.INVALID_TAB_INDEX);
                            return tab;
                        });
        Assert.assertEquals(title, ChromeTabUtils.getTitleOnUiThread(bgTab));

        // Verify that the background tab is not loading.
        Assert.assertFalse(bgTab.isLoading());

        // Switch tabs and verify that the tab is loaded as it gets foregrounded.
        Runnable loadPage =
                () -> {
                    ThreadUtils.runOnUiThreadBlocking(
                            () -> {
                                TabModelUtils.setIndex(
                                        sActivityTestRule.getActivity().getCurrentTabModel(),
                                        indexOf(bgTab));
                            });
                };
        ChromeTabUtils.waitForTabPageLoaded(bgTab, url, loadPage);
        Assert.assertNotNull(bgTab.getView());

        // Title should change when the page loads.
        Assert.assertNotEquals(title, ChromeTabUtils.getTitleOnUiThread(bgTab));
    }

    private Intent createIntent(int tabIndex) {
        Intent intent = new Intent();
        intent.putExtra(IntentHandler.EXTRA_TAB_INDEX, tabIndex);
        return intent;
    }

    /**
     * @return the index of the given tab in the current tab model
     */
    private int indexOf(Tab tab) {
        return sActivityTestRule.getActivity().getCurrentTabModel().indexOf(tab);
    }
}