chromium/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java

// Copyright 2017 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.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import android.text.TextUtils;

import androidx.test.filters.SmallTest;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import org.chromium.base.Token;
import org.chromium.base.UserDataHost;
import org.chromium.base.task.TaskRunner;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tab.TabState;
import org.chromium.chrome.browser.tab.TabStateAttributes;
import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabModelSelectorMetadata;
import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabRestoreDetails;
import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.url.GURL;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/** Unit tests for the tab persistent store logic. */
@RunWith(BaseRobolectricTestRunner.class)
@Batch(Batch.UNIT_TESTS)
public class TabPersistentStoreUnitTest {
    private static final Integer RESTORE_TAB_ID_1 = 31;
    private static final Integer RESTORE_TAB_ID_2 = 32;
    private static final Integer RESTORE_TAB_ID_3 = 33;

    private static final String REGULAR_TAB_STRING_1 = "https://foo.com/";
    private static final String INCOGNITO_TAB_STRING_1 = "https://bar.com/";
    private static final String INCOGNITO_TAB_STRING_2 = "https://baz.com/";
    private static final String RESTORE_TAB_STRING_1 = "https://qux.com/";
    private static final String RESTORE_TAB_STRING_2 = "https://quux.com/";
    private static final String RESTORE_TAB_STRING_3 = "https://quuz.com/";

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

    @Mock private TabPersistencePolicy mPersistencePolicy;
    @Mock private TabModelSelector mTabModelSelector;
    @Mock private TabModel mNormalTabModel;
    @Mock private TabModel mIncognitoTabModel;
    @Mock private TabModelFilterProvider mTabModelFilterProvider;
    @Mock private TabCreatorManager mTabCreatorManager;
    @Mock private TabCreator mNormalTabCreator;
    @Mock private TabCreator mIncognitoTabCreator;
    @Mock private TabWindowManager mTabWindowManager;

    private TabModelFilter mNormalTabModelFilter;
    private TabModelFilter mIncognitoTabModelFilter;
    private TabPersistentStore mPersistentStore;

    @Before
    public void setUp() {
        when(mIncognitoTabModel.isIncognito()).thenReturn(true);
        when(mTabModelSelector.getModel(false)).thenReturn(mNormalTabModel);
        when(mTabModelSelector.getModel(true)).thenReturn(mIncognitoTabModel);

        when(mTabCreatorManager.getTabCreator(false)).thenReturn(mNormalTabCreator);
        when(mTabCreatorManager.getTabCreator(true)).thenReturn(mIncognitoTabCreator);

        when(mPersistencePolicy.getMetadataFileName())
                .thenReturn(TabPersistentStore.SAVED_METADATA_FILE_PREFIX + "state_files_yay");
        when(mPersistencePolicy.isMergeInProgress()).thenReturn(false);
        when(mPersistencePolicy.performInitialization(any(TaskRunner.class))).thenReturn(false);

        when(mTabModelSelector.getTabModelFilterProvider()).thenReturn(mTabModelFilterProvider);
        mNormalTabModelFilter = new TabGroupModelFilter(mNormalTabModel);
        mIncognitoTabModelFilter = new TabGroupModelFilter(mIncognitoTabModel);
        when(mTabModelFilterProvider.getTabModelFilter(false)).thenReturn(mNormalTabModelFilter);
        when(mTabModelFilterProvider.getTabModelFilter(true)).thenReturn(mIncognitoTabModelFilter);
    }

    @After
    public void tearDown() throws Exception {
        // Flush pending PersistentStore tasks.
        final AtomicBoolean flushed = new AtomicBoolean(false);
        if (mPersistentStore != null) {
            mPersistentStore.getTaskRunnerForTests().postTask(() -> flushed.set(true));
            CriteriaHelper.pollUiThreadForJUnit(() -> flushed.get());
        }
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testNtpSaveBehavior() {
        when(mNormalTabModel.index()).thenReturn(TabList.INVALID_TAB_INDEX);
        when(mIncognitoTabModel.index()).thenReturn(TabList.INVALID_TAB_INDEX);

        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager) {
                    @Override
                    protected void saveNextTab() {
                        // Intentionally ignore to avoid triggering async task creation.
                    }
                };

        Tab emptyNtpTab = mock(Tab.class);
        UserDataHost emptyNtpTabUserDataHost = new UserDataHost();
        when(emptyNtpTab.getUserDataHost()).thenReturn(emptyNtpTabUserDataHost);
        TabStateAttributes.createForTab(emptyNtpTab, TabCreationState.FROZEN_ON_RESTORE);
        when(emptyNtpTab.getUrl()).thenReturn(new GURL(UrlConstants.NTP_URL));
        TabStateAttributes.from(emptyNtpTab)
                .setStateForTesting(TabStateAttributes.DirtinessState.DIRTY);

        mPersistentStore.addTabToSaveQueue(emptyNtpTab);
        assertTrue(mPersistentStore.isTabPendingSave(emptyNtpTab));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testNotActiveEmptyNtpNotIgnoredDuringRestore() {
        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager);
        mPersistentStore.initializeRestoreVars(false);

        TabRestoreDetails emptyNtpDetails =
                new TabRestoreDetails(1, 0, false, UrlConstants.NTP_URL, false);
        mPersistentStore.restoreTab(emptyNtpDetails, null, false);

        verify(mNormalTabCreator)
                .createNewTab(
                        argThat(new LoadUrlParamsUrlMatcher(UrlConstants.NTP_URL)),
                        eq(TabLaunchType.FROM_RESTORE),
                        isNull(),
                        eq(0));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testActiveEmptyNtpNotIgnoredDuringRestore() {
        when(mTabModelSelector.isIncognitoSelected()).thenReturn(false);
        when(mTabModelSelector.getCurrentModel()).thenReturn(mNormalTabModel);

        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager);
        mPersistentStore.initializeRestoreVars(false);

        LoadUrlParamsUrlMatcher paramsMatcher = new LoadUrlParamsUrlMatcher(UrlConstants.NTP_URL);
        Tab emptyNtp = mock(Tab.class);
        when(mNormalTabCreator.createNewTab(
                        argThat(paramsMatcher), eq(TabLaunchType.FROM_RESTORE), isNull()))
                .thenReturn(emptyNtp);

        TabRestoreDetails emptyNtpDetails =
                new TabRestoreDetails(1, 0, false, UrlConstants.NTP_URL, false);
        mPersistentStore.restoreTab(emptyNtpDetails, null, true);

        verify(mNormalTabCreator)
                .createNewTab(
                        argThat(new LoadUrlParamsUrlMatcher(UrlConstants.NTP_URL)),
                        eq(TabLaunchType.FROM_RESTORE),
                        isNull(),
                        eq(0));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testNtpFromMergeWithNoStateNotIgnoredDuringMerge() {
        when(mTabModelSelector.isIncognitoSelected()).thenReturn(false);
        when(mTabModelSelector.getCurrentModel()).thenReturn(mNormalTabModel);

        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager);
        mPersistentStore.initializeRestoreVars(false);

        LoadUrlParamsUrlMatcher paramsMatcher = new LoadUrlParamsUrlMatcher(UrlConstants.NTP_URL);
        Tab emptyNtp = mock(Tab.class);
        when(mNormalTabCreator.createNewTab(
                        argThat(paramsMatcher), eq(TabLaunchType.FROM_RESTORE), isNull()))
                .thenReturn(emptyNtp);

        TabRestoreDetails emptyNtpDetails =
                new TabRestoreDetails(1, 0, false, UrlConstants.NTP_URL, true);
        mPersistentStore.restoreTab(emptyNtpDetails, null, false);
        verify(mNormalTabCreator)
                .createNewTab(
                        argThat(new LoadUrlParamsUrlMatcher(UrlConstants.NTP_URL)),
                        eq(TabLaunchType.FROM_RESTORE),
                        isNull(),
                        eq(0));

        TabRestoreDetails emptyIncognitoNtpDetails =
                new TabRestoreDetails(1, 0, true, UrlConstants.NTP_URL, true);
        mPersistentStore.restoreTab(emptyIncognitoNtpDetails, null, false);
        verify(mIncognitoTabCreator)
                .createNewTab(
                        argThat(new LoadUrlParamsUrlMatcher(UrlConstants.NTP_URL)),
                        eq(TabLaunchType.FROM_RESTORE),
                        isNull(),
                        eq(0));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testNtpWithStateNotIgnoredDuringRestore() {
        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager);
        mPersistentStore.initializeRestoreVars(false);

        TabRestoreDetails ntpDetails =
                new TabRestoreDetails(1, 0, false, UrlConstants.NTP_URL, false);
        TabState ntpState = new TabState();
        mPersistentStore.restoreTab(ntpDetails, ntpState, false);

        verify(mNormalTabCreator).createFrozenTab(eq(ntpState), eq(1), anyInt());
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testActiveEmptyIncognitoNtpNotIgnoredDuringRestore() {
        when(mTabModelSelector.isIncognitoSelected()).thenReturn(true);
        when(mTabModelSelector.getCurrentModel()).thenReturn(mIncognitoTabModel);

        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager);
        mPersistentStore.initializeRestoreVars(false);

        LoadUrlParamsUrlMatcher paramsMatcher = new LoadUrlParamsUrlMatcher(UrlConstants.NTP_URL);
        Tab emptyNtp = mock(Tab.class);
        when(mIncognitoTabCreator.createNewTab(
                        argThat(paramsMatcher), eq(TabLaunchType.FROM_RESTORE), isNull()))
                .thenReturn(emptyNtp);

        TabRestoreDetails emptyNtpDetails =
                new TabRestoreDetails(1, 0, true, UrlConstants.NTP_URL, false);
        mPersistentStore.restoreTab(emptyNtpDetails, null, true);

        verify(mIncognitoTabCreator)
                .createNewTab(
                        argThat(new LoadUrlParamsUrlMatcher(UrlConstants.NTP_URL)),
                        eq(TabLaunchType.FROM_RESTORE),
                        isNull(),
                        eq(0));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testNotActiveIncognitoNtpIgnoredDuringRestore() {
        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager);
        mPersistentStore.initializeRestoreVars(false);

        TabRestoreDetails emptyNtpDetails =
                new TabRestoreDetails(1, 0, true, UrlConstants.NTP_URL, false);
        mPersistentStore.restoreTab(emptyNtpDetails, null, false);

        verifyNoMoreInteractions(mIncognitoTabCreator);
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testActiveEmptyIncognitoNtpIgnoredDuringRestoreIfIncognitoLoadingIsDisabled() {
        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager);
        mPersistentStore.initializeRestoreVars(true);

        TabRestoreDetails emptyNtpDetails =
                new TabRestoreDetails(1, 0, true, UrlConstants.NTP_URL, false);
        mPersistentStore.restoreTab(emptyNtpDetails, null, true);

        verifyNoMoreInteractions(mIncognitoTabCreator);
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    @EnableFeatures(ChromeFeatureList.ANDROID_TAB_DECLUTTER_DEDUPE_TAB_IDS_KILL_SWITCH)
    public void testDuplicateTabIds() {
        mPersistentStore =
                new TabPersistentStore(
                        TabPersistentStore.CLIENT_TAG_REGULAR,
                        mPersistencePolicy,
                        mTabModelSelector,
                        mTabCreatorManager,
                        mTabWindowManager);
        mPersistentStore.initializeRestoreVars(false);

        TabRestoreDetails regularTabRestoreDetails =
                new TabRestoreDetails(RESTORE_TAB_ID_1, 2, false, RESTORE_TAB_STRING_1, false);
        TabRestoreDetails regularTabRestoreDetailsDupe =
                new TabRestoreDetails(RESTORE_TAB_ID_1, 2, false, RESTORE_TAB_STRING_1, false);
        TabState state = new TabState();
        mPersistentStore.restoreTab(regularTabRestoreDetails, state, false);
        mPersistentStore.restoreTab(regularTabRestoreDetailsDupe, state, false);

        // Restoring a dupe should only create a single tab, and skip the second.
        verify(mNormalTabCreator, times(1)).createFrozenTab(eq(state), eq(RESTORE_TAB_ID_1), eq(0));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testSerializeTabModelSelector() throws IOException {
        setupSerializationTestMocks();
        TabModelSelectorMetadata metadata =
                TabPersistentStore.saveTabModelSelectorMetadata(mTabModelSelector, null);

        assertEquals("Incorrect index for regular", 0, metadata.normalModelMetadata.index);
        // Verifies that the non-active NTP isn't saved.
        assertEquals(
                "Incorrect number of tabs in regular", 1, metadata.normalModelMetadata.ids.size());
        assertEquals(
                "Incorrect URL for regular tab.",
                REGULAR_TAB_STRING_1,
                metadata.normalModelMetadata.urls.get(0));

        assertEquals("Incorrect index for incognito", 1, metadata.incognitoModelMetadata.index);
        assertEquals(
                "Incorrect number of tabs in incognito",
                2,
                metadata.incognitoModelMetadata.ids.size());
        assertEquals(
                "Incorrect URL for first incognito tab.",
                INCOGNITO_TAB_STRING_1,
                metadata.incognitoModelMetadata.urls.get(0));
        assertEquals(
                "Incorrect URL for second incognito tab.",
                INCOGNITO_TAB_STRING_2,
                metadata.incognitoModelMetadata.urls.get(1));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testSkipNonActiveNtpsWithSkippedNtpComeBeforeActiveTab() throws IOException {
        setupSerializationTestMocksWithSkippedNtpComeBeforeActiveTab();
        TabModelSelectorMetadata metadata =
                TabPersistentStore.saveTabModelSelectorMetadata(mTabModelSelector, null);

        assertEquals("Incorrect index for regular", 0, metadata.normalModelMetadata.index);
        assertEquals(
                "Incorrect number of tabs in regular", 1, metadata.normalModelMetadata.ids.size());
        assertEquals(
                "Incorrect URL for regular tab.",
                REGULAR_TAB_STRING_1,
                metadata.normalModelMetadata.urls.get(0));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testSkipNonActiveNtpsWithSkippedNtpComeAfterActiveTab() throws IOException {
        setupSerializationTestMocks();
        TabModelSelectorMetadata metadata =
                TabPersistentStore.saveTabModelSelectorMetadata(mTabModelSelector, null);

        assertEquals("Incorrect index for regular", 0, metadata.normalModelMetadata.index);
        assertEquals(
                "Incorrect number of tabs in regular", 1, metadata.normalModelMetadata.ids.size());
        assertEquals(
                "Incorrect URL for regular tab.",
                REGULAR_TAB_STRING_1,
                metadata.normalModelMetadata.urls.get(0));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testSkipNonActiveNtpsWithGroupedAndNavigableNtps_TabGroupStableIdsEnabled()
            throws IOException {
        setupSerializationTestMocksWithGroupedAndNavigableNtps();
        TabModelSelectorMetadata metadata =
                TabPersistentStore.saveTabModelSelectorMetadata(mTabModelSelector, null);

        assertEquals("Incorrect index for regular", 1, metadata.normalModelMetadata.index);
        assertEquals(
                "Incorrect number of tabs in regular", 2, metadata.normalModelMetadata.ids.size());
        assertEquals(
                "Incorrect URL for first NTP.",
                UrlConstants.NTP_URL,
                metadata.normalModelMetadata.urls.get(0));
        assertEquals(
                "Incorrect id for first NTP.",
                1,
                metadata.normalModelMetadata.ids.get(0).intValue());
        assertEquals(
                "Incorrect URL for regular tab.",
                REGULAR_TAB_STRING_1,
                metadata.normalModelMetadata.urls.get(1));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testSerializeTabModelSelector_tabsBeingRestored() throws IOException {
        setupSerializationTestMocks();
        TabRestoreDetails regularTabRestoreDetails =
                new TabRestoreDetails(RESTORE_TAB_ID_1, 2, false, RESTORE_TAB_STRING_1, false);
        TabRestoreDetails incognitoTabRestoreDetails =
                new TabRestoreDetails(RESTORE_TAB_ID_2, 3, true, RESTORE_TAB_STRING_2, false);
        TabRestoreDetails unknownTabRestoreDetails =
                new TabRestoreDetails(RESTORE_TAB_ID_3, 4, null, RESTORE_TAB_STRING_3, false);
        List<TabRestoreDetails> tabRestoreDetails = new ArrayList<>();
        tabRestoreDetails.add(regularTabRestoreDetails);
        tabRestoreDetails.add(incognitoTabRestoreDetails);
        tabRestoreDetails.add(unknownTabRestoreDetails);

        TabModelSelectorMetadata metadata =
                TabPersistentStore.saveTabModelSelectorMetadata(
                        mTabModelSelector, tabRestoreDetails);
        assertEquals("Incorrect index for regular", 0, metadata.normalModelMetadata.index);
        assertEquals(
                "Incorrect number of tabs in regular", 2, metadata.normalModelMetadata.ids.size());
        assertEquals(
                "Incorrect URL for first regular tab.",
                REGULAR_TAB_STRING_1,
                metadata.normalModelMetadata.urls.get(0));
        assertEquals(
                "Incorrect URL for first second tab.",
                RESTORE_TAB_STRING_1,
                metadata.normalModelMetadata.urls.get(1));

        // TabRestoreDetails with unknown isIncognito should be appended to incognito list.
        assertEquals("Incorrect index for incognito", 1, metadata.incognitoModelMetadata.index);
        assertEquals(
                "Incorrect number of tabs in incognito",
                4,
                metadata.incognitoModelMetadata.ids.size());
        assertEquals(
                "Incorrect URL for first incognito tab.",
                INCOGNITO_TAB_STRING_1,
                metadata.incognitoModelMetadata.urls.get(0));
        assertEquals(
                "Incorrect URL for second incognito tab.",
                INCOGNITO_TAB_STRING_2,
                metadata.incognitoModelMetadata.urls.get(1));
        assertEquals(
                "Incorrect URL for third incognito tab.",
                RESTORE_TAB_STRING_2,
                metadata.incognitoModelMetadata.urls.get(2));
        assertEquals(
                "Incorrect URL for fourth \"incognito\" tab.",
                RESTORE_TAB_STRING_3,
                metadata.incognitoModelMetadata.urls.get(3));
    }

    @Test
    @SmallTest
    @Feature("TabPersistentStore")
    public void testSerializeTabModelSelector_closingTabsSkipped() throws IOException {
        when(mNormalTabModel.getCount()).thenReturn(2);
        when(mNormalTabModel.index()).thenReturn(1);
        Tab regularTab1 = mock(Tab.class);
        when(regularTab1.getId()).thenReturn(11);
        when(regularTab1.getUrl()).thenReturn(new GURL(REGULAR_TAB_STRING_1));
        when(regularTab1.isClosing()).thenReturn(false);
        when(mNormalTabModel.getTabAt(0)).thenReturn(regularTab1);
        Tab regularTab2 = mock(Tab.class);
        when(regularTab2.getId()).thenReturn(22);
        when(regularTab2.getUrl()).thenReturn(new GURL(RESTORE_TAB_STRING_2));
        when(regularTab2.isClosing()).thenReturn(true);
        when(mNormalTabModel.getTabAt(1)).thenReturn(regularTab2);
        when(mTabModelSelector.getTotalTabCount()).thenReturn(2);

        TabModelSelectorMetadata metadata =
                TabPersistentStore.saveTabModelSelectorMetadata(mTabModelSelector, null);

        assertEquals(1, metadata.normalModelMetadata.ids.size());
        assertEquals(1, metadata.normalModelMetadata.urls.size());
        assertEquals(0, metadata.normalModelMetadata.index);
        assertEquals(11, metadata.normalModelMetadata.ids.get(0).intValue());
        assertEquals(REGULAR_TAB_STRING_1, metadata.normalModelMetadata.urls.get(0));
    }

    private void setupSerializationTestMocks() {
        when(mNormalTabModel.getCount()).thenReturn(2);
        when(mNormalTabModel.index()).thenReturn(0);
        Tab regularTab1 = mock(Tab.class);
        GURL gurl = new GURL(REGULAR_TAB_STRING_1);
        when(regularTab1.getUrl()).thenReturn(gurl);
        when(mNormalTabModel.getTabAt(0)).thenReturn(regularTab1);

        Tab regularNtpTab1 = mock(Tab.class);
        GURL ntpGurl = new GURL(UrlConstants.NTP_URL);
        when(regularNtpTab1.getUrl()).thenReturn(ntpGurl);
        when(regularNtpTab1.isNativePage()).thenReturn(true);
        when(mNormalTabModel.getTabAt(1)).thenReturn(regularNtpTab1);

        when(mIncognitoTabModel.getCount()).thenReturn(2);
        when(mIncognitoTabModel.index()).thenReturn(1);
        Tab incognitoTab1 = mock(Tab.class);
        gurl = new GURL(INCOGNITO_TAB_STRING_1);
        when(incognitoTab1.getUrl()).thenReturn(gurl);
        when(incognitoTab1.isIncognito()).thenReturn(true);
        when(mIncognitoTabModel.getTabAt(0)).thenReturn(incognitoTab1);

        Tab incognitoTab2 = mock(Tab.class);
        gurl = new GURL(INCOGNITO_TAB_STRING_2);
        when(incognitoTab2.getUrl()).thenReturn(gurl);
        when(incognitoTab2.isIncognito()).thenReturn(true);
        when(mIncognitoTabModel.getTabAt(1)).thenReturn(incognitoTab2);

        when(mTabModelSelector.getTotalTabCount()).thenReturn(4);
    }

    private void setupSerializationTestMocksWithSkippedNtpComeBeforeActiveTab() {
        when(mNormalTabModel.getCount()).thenReturn(2);
        // Sets a non active Ntp is the first Tab, and a regular Tab is the second one and the
        // current active Tab.
        when(mNormalTabModel.index()).thenReturn(1);
        Tab regularNtpTab1 = mock(Tab.class);
        GURL ntpGurl = new GURL(UrlConstants.NTP_URL);
        when(regularNtpTab1.getUrl()).thenReturn(ntpGurl);
        when(regularNtpTab1.isNativePage()).thenReturn(true);
        when(mNormalTabModel.getTabAt(0)).thenReturn(regularNtpTab1);

        Tab regularTab1 = mock(Tab.class);
        GURL gurl = new GURL(REGULAR_TAB_STRING_1);
        when(regularTab1.getUrl()).thenReturn(gurl);
        when(mNormalTabModel.getTabAt(1)).thenReturn(regularTab1);

        when(mIncognitoTabModel.getCount()).thenReturn(0);
    }

    private void setupSerializationTestMocksWithGroupedAndNavigableNtps() {
        when(mNormalTabModel.getCount()).thenReturn(3);
        when(mNormalTabModel.index()).thenReturn(2);

        GURL ntpGurl = new GURL(UrlConstants.NTP_URL);

        // Non-active NTP with no state.
        Tab regularNtpTab1 = mock(Tab.class);
        when(regularNtpTab1.getId()).thenReturn(0);
        when(regularNtpTab1.getUrl()).thenReturn(ntpGurl);
        when(regularNtpTab1.isNativePage()).thenReturn(true);
        when(mNormalTabModel.getTabAt(0)).thenReturn(regularNtpTab1);

        // Non-active NTP with tab group.
        Tab regularNtpTab2 = mock(Tab.class);
        when(regularNtpTab2.getId()).thenReturn(1);
        when(regularNtpTab2.getUrl()).thenReturn(ntpGurl);
        when(regularNtpTab2.isNativePage()).thenReturn(true);
        when(regularNtpTab2.getTabGroupId()).thenReturn(new Token(1L, 2L));
        when(mNormalTabModel.getTabAt(1)).thenReturn(regularNtpTab2);

        // Regular selected tab.
        Tab regularTab1 = mock(Tab.class);
        GURL gurl = new GURL(REGULAR_TAB_STRING_1);
        when(regularTab1.getUrl()).thenReturn(gurl);
        when(mNormalTabModel.getTabAt(2)).thenReturn(regularTab1);

        when(mIncognitoTabModel.getCount()).thenReturn(0);
    }

    private static class LoadUrlParamsUrlMatcher implements ArgumentMatcher<LoadUrlParams> {
        private final String mUrl;

        LoadUrlParamsUrlMatcher(String url) {
            mUrl = url;
        }

        @Override
        public boolean matches(LoadUrlParams argument) {
            return TextUtils.equals(mUrl, argument.getUrl());
        }
    }
}