chromium/chrome/android/junit/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestratorUnitTest.java

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

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelperManager.TabModelStartupInfo;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorBase;
import org.chromium.chrome.browser.tabmodel.TabPersistentStore;
import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabPersistentStoreObserver;

/** Tests for {@link TabModelOrchestrator} */
@RunWith(BaseRobolectricTestRunner.class)
public class TabModelOrchestratorUnitTest {
    @Mock private ObservableSupplierImpl<TabModelStartupInfo> mMockTabModelStartupInfoSupplier;
    @Mock private TabModel mMockTabModel;
    @Mock private TabModelSelectorBase mMockTabModelSelectorBase;
    @Mock private TabPersistentStore mMockTabPersistentStore;

    private TabModelOrchestrator mTabModelOrchestrator;
    private ArgumentCaptor<TabPersistentStoreObserver> mObserverCaptor;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        when(mMockTabModelSelectorBase.getModel(anyBoolean())).thenReturn(mMockTabModel);

        mTabModelOrchestrator =
                new TabModelOrchestrator() {
                    @Override
                    public TabModelSelectorBase getTabModelSelector() {
                        return mMockTabModelSelectorBase;
                    }
                };
        mTabModelOrchestrator.setTabPersistentStoreForTesting(mMockTabPersistentStore);

        mObserverCaptor = ArgumentCaptor.forClass(TabPersistentStoreObserver.class);
        mTabModelOrchestrator.wireSelectorAndStore();
        mTabModelOrchestrator.setStartupInfoObservableSupplier(mMockTabModelStartupInfoSupplier);
        verify(mMockTabPersistentStore).addObserver(mObserverCaptor.capture());
    }

    @Test
    @SmallTest
    @Feature({"TabStripPerformance"})
    public void testTabModelStartupInfo() {
        // Send test tab model info.
        int numIncognitoTabs = 2;
        int numStandardTabs = 3;
        int incognitoIndex = 1;
        int standardIndex = 2;
        boolean fromMerge = false;
        readTabState(numStandardTabs, numIncognitoTabs, standardIndex, incognitoIndex, fromMerge);

        // Verify that the {@link TabModelStartupInfo} is as expected.
        ArgumentCaptor<TabModelStartupInfo> startupInfoCaptor =
                ArgumentCaptor.forClass(TabModelStartupInfo.class);
        verify(mMockTabModelStartupInfoSupplier).set(startupInfoCaptor.capture());
        TabModelStartupInfo startupInfo = startupInfoCaptor.getValue();

        assertEquals("Unexpected standard tab count.", numStandardTabs, startupInfo.standardCount);
        assertEquals(
                "Unexpected incognito tab count.", numIncognitoTabs, startupInfo.incognitoCount);
        assertEquals(
                "Unexpected standard active tab index.",
                standardIndex,
                startupInfo.standardActiveIndex);
        assertEquals(
                "Unexpected incognito active tab index.",
                incognitoIndex,
                startupInfo.incognitoActiveIndex);
    }

    @Test
    @SmallTest
    @Feature({"TabStripPerformance"})
    public void testTabModelStartupInfo_FromMerge() {
        // Send test tab model info.
        int numIncognitoTabs = 2;
        int numStandardTabs = 3;
        int incognitoIndex = 1;
        int standardIndex = 2;
        boolean fromMerge = true;
        readTabState(numStandardTabs, numIncognitoTabs, standardIndex, incognitoIndex, fromMerge);

        // Verify that the {@link TabModelStartupInfo} is as expected.
        ArgumentCaptor<TabModelStartupInfo> startupInfoCaptor =
                ArgumentCaptor.forClass(TabModelStartupInfo.class);
        verify(mMockTabModelStartupInfoSupplier).set(startupInfoCaptor.capture());
        TabModelStartupInfo startupInfo = startupInfoCaptor.getValue();

        assertEquals("Unexpected standard tab count.", numStandardTabs, startupInfo.standardCount);
        assertEquals(
                "Unexpected incognito tab count.", numIncognitoTabs, startupInfo.incognitoCount);
        assertEquals(
                "Merged tab states shouldn't set the standard active tab index.",
                TabModel.INVALID_TAB_INDEX,
                startupInfo.standardActiveIndex);
        assertEquals(
                "Merged tab states shouldn't set the incognito active tab index.",
                TabModel.INVALID_TAB_INDEX,
                startupInfo.incognitoActiveIndex);
    }

    @Test
    @SmallTest
    @Feature({"TabStripPerformance"})
    public void testTabModelStartupInfo_IgnoreIncognito() {
        mTabModelOrchestrator.loadState(true, null);

        // Send test tab model info.
        int numIncognitoTabs = 2;
        int numStandardTabs = 3;
        int incognitoIndex = 1;
        int standardIndex = 2;
        boolean fromMerge = false;
        readTabState(numStandardTabs, numIncognitoTabs, standardIndex, incognitoIndex, fromMerge);

        // Verify that the {@link TabModelStartupInfo} is as expected.
        ArgumentCaptor<TabModelStartupInfo> startupInfoCaptor =
                ArgumentCaptor.forClass(TabModelStartupInfo.class);
        verify(mMockTabModelStartupInfoSupplier).set(startupInfoCaptor.capture());
        TabModelStartupInfo startupInfo = startupInfoCaptor.getValue();

        assertEquals("Unexpected standard tab count.", numStandardTabs, startupInfo.standardCount);
        assertEquals("Unexpected incognito tab count.", 0, startupInfo.incognitoCount);
        assertEquals(
                "Unexpected standard active tab index.",
                standardIndex,
                startupInfo.standardActiveIndex);
        assertEquals(
                "Unexpected incognito active tab index.",
                TabModel.INVALID_TAB_INDEX,
                startupInfo.incognitoActiveIndex);
    }

    private void readTabState(
            int numStandardTabs,
            int numIncognitoTabs,
            int standardIndex,
            int incognitoIndex,
            boolean fromMerge) {
        TabPersistentStoreObserver observer = mObserverCaptor.getValue();
        int index = 0;

        for (int i = 0; i < numIncognitoTabs; i++) {
            observer.onDetailsRead(
                    index, index, "Tab " + index, false, i == incognitoIndex, true, fromMerge);
            index++;
        }

        for (int i = 0; i < numStandardTabs; i++) {
            observer.onDetailsRead(
                    index, index, "Tab " + index, i == standardIndex, false, false, fromMerge);
            index++;
        }

        observer.onInitialized(numIncognitoTabs + numStandardTabs);
        mTabModelOrchestrator.restoreTabs(false);
    }
}