chromium/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabUsageTrackerTest.java

// Copyright 2022 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.compositor.overlays.strip;

import androidx.test.filters.SmallTest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;

import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.TabUsageTracker;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
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.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;

/**
 * Test suite to verify that the TabUsageTracker correctly records the number of tabs used and the
 * percentage of tabs used.
 */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class TabUsageTrackerTest {
    @Mock TabModelSelector mTabModelSelector;
    @Mock ActivityLifecycleDispatcher mDispatcher;
    @Mock TabModel mTabModel;

    private static final int INITIAL_TAB_COUNT = 0;
    private static final String NUMBER_OF_TABS_USED = "Android.ActivityStop.NumberOfTabsUsed";
    private static final String PERCENTAGE_OF_TABS_USED =
            "Android.ActivityStop.PercentageOfTabsUsed";

    private TabUsageTracker mTabUsageTracker;

    @Before
    public void setUp() throws TimeoutException {
        MockitoAnnotations.initMocks(this);
        List<TabModel> tabModels = new ArrayList<>();
        tabModels.add(mTabModel);

        Mockito.when(mTabModel.getCount()).thenReturn(INITIAL_TAB_COUNT);
        Mockito.when(mTabModelSelector.getCurrentModel()).thenReturn(mTabModel);
        Mockito.when(mTabModelSelector.getModels()).thenReturn(tabModels);
        Mockito.when(mTabModelSelector.isTabStateInitialized()).thenReturn(true);

        mTabUsageTracker = new TabUsageTracker(mDispatcher, mTabModelSelector);
    }

    @Test
    @SmallTest
    public void testOnStop_RecordsHistogram_NoInitialTabs() {
        // Arrange
        mTabUsageTracker.onResumeWithNative();
        Tab tab1 = getMockedTab(1);
        Tab tab2 = getMockedTab(2);

        // Act: Create 2 tabs, select 1 tab and call onStop.
        TabModelSelectorTabModelObserver observer =
                mTabUsageTracker.getTabModelSelectorTabModelObserverForTests();
        observer.didAddTab(
                tab1, TabLaunchType.FROM_CHROME_UI, TabCreationState.LIVE_IN_FOREGROUND, false);
        observer.didAddTab(
                tab2, TabLaunchType.FROM_CHROME_UI, TabCreationState.LIVE_IN_FOREGROUND, false);
        observer.didSelectTab(tab1, TabLaunchType.FROM_CHROME_UI, 0);

        mTabUsageTracker.onStopWithNative();

        // Assert
        Assert.assertEquals(
                1, RecordHistogram.getHistogramValueCountForTesting(NUMBER_OF_TABS_USED, 1));
        Assert.assertEquals(
                1, RecordHistogram.getHistogramValueCountForTesting(PERCENTAGE_OF_TABS_USED, 50));
    }

    @Test
    @SmallTest
    public void testOnStop_RecordsHistogram_HasInitialTabs() {
        // Arrange
        Tab tab1 = getMockedTab(1);
        Tab selectedTab = getMockedTab(3);
        // Start with 5 existing tabs and 1 selected tab.
        Mockito.when(mTabModelSelector.getTotalTabCount()).thenReturn(5);
        Mockito.when(mTabModelSelector.getCurrentTab()).thenReturn(selectedTab);
        mTabUsageTracker.onResumeWithNative();

        // Act: Create 1 tab, select 1 tab and call onStop.
        TabModelSelectorTabModelObserver observer =
                mTabUsageTracker.getTabModelSelectorTabModelObserverForTests();
        observer.didAddTab(
                tab1, TabLaunchType.FROM_CHROME_UI, TabCreationState.LIVE_IN_FOREGROUND, false);
        observer.didSelectTab(tab1, TabLaunchType.FROM_CHROME_UI, 3);

        mTabUsageTracker.onStopWithNative();

        // Assert that number of tabs used is 2 and percentage is 2/6 * 100 = 33
        Assert.assertEquals(
                1, RecordHistogram.getHistogramValueCountForTesting(NUMBER_OF_TABS_USED, 2));
        Assert.assertEquals(
                1, RecordHistogram.getHistogramValueCountForTesting(PERCENTAGE_OF_TABS_USED, 33));
    }

    @Test
    @SmallTest
    public void testOnStop_CalledBeforeOnResume_DoesNotRecordHistogram() {
        mTabUsageTracker.onStopWithNative();

        Assert.assertEquals(
                0, RecordHistogram.getHistogramTotalCountForTesting(NUMBER_OF_TABS_USED));
        Assert.assertEquals(
                0, RecordHistogram.getHistogramTotalCountForTesting(PERCENTAGE_OF_TABS_USED));
    }

    @Test
    @SmallTest
    public void testOnDestroy() {
        mTabUsageTracker.onDestroy();

        Mockito.verify(mDispatcher).unregister(mTabUsageTracker);
    }

    private Tab getMockedTab(int id) {
        Tab tab = Mockito.mock(Tab.class);
        Mockito.when(tab.getId()).thenReturn(id);
        return tab;
    }
}