chromium/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/UiUtilsUnitTest.java

// Copyright 2021 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.multiwindow;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;

import androidx.annotation.DrawableRes;

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

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.components.favicon.LargeIconBridge;

/** Tests for {@link UiUtils}. */
@RunWith(BaseRobolectricTestRunner.class)
public class UiUtilsUnitTest {
    // Title
    private static final String TITLE = "Title";
    private static final String EMPTY_WINDOW = "Empty Window";
    private static final String INCOGNITO = "Incognito";

    // Description
    private static final String NO_TABS = "No tabs";
    private static final String CURRENT = "Current";
    private static final String OPEN = "Window is open";

    @Mock Context mContext;
    @Mock Resources mResources;
    @Mock Drawable mDrawable;
    @Mock LargeIconBridge mIconBridge;

    UiUtils mUiUtils;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        doReturn(mResources).when(mContext).getResources();
        doReturn(mDrawable).when(mResources).getDrawable(anyInt(), any());
        doReturn(EMPTY_WINDOW)
                .when(mResources)
                .getString(R.string.instance_switcher_entry_empty_window);
        doReturn(INCOGNITO).when(mResources).getString(R.string.notification_incognito_tab);
        doReturn(NO_TABS).when(mResources).getString(R.string.instance_switcher_tab_count_zero);

        doReturn(CURRENT).when(mResources).getString(R.string.instance_switcher_current_window);
        doReturn(OPEN).when(mResources).getString(R.string.instance_switcher_adjacent_window);
        mUiUtils =
                new UiUtils(mContext, mIconBridge) {
                    @Override
                    Drawable getTintedIcon(@DrawableRes int drawableId) {
                        return null;
                    }
                };
    }

    @Test
    public void testItemTitle() {
        // Empty window
        assertEquals(
                "Instance with no tabs has a wrong title",
                EMPTY_WINDOW,
                mUiUtils.getItemTitle(mockInstance(57, 0, 0, false)));

        assertEquals(
                "Instance should be empty if not initialized yet",
                EMPTY_WINDOW,
                mUiUtils.getItemTitle(mockInstanceBeforeLoadingTab(57)));

        // Normal tabs only -> TITLE
        assertEquals(
                "Instance with normal tabs has a wrong title",
                TITLE,
                mUiUtils.getItemTitle(mockInstance(57, 1, 0, false)));

        // Incognito selected -> Incognito, regardless of # of normal tabs
        assertEquals(
                "Instance with only incognito tabs has a wrong title",
                INCOGNITO,
                mUiUtils.getItemTitle(mockInstance(57, 1, 1, true)));
        assertEquals(
                "Instance with only incognito tabs has a wrong title",
                INCOGNITO,
                mUiUtils.getItemTitle(mockInstance(57, 0, 1, true)));

        // Incognito-selected, mixed tabs, killed task -> TITLE (use normal tab info)
        assertEquals(
                "Incognito-selected, mixed tab, killed task should show normal tab title",
                TITLE,
                mUiUtils.getItemTitle(mockInstance(-1, 1, 1, true)));
    }

    @Test
    public void testItemDescription() {
        // Empty window -> No tabs
        assertEquals(
                "Instance with no tabs has a wrong description",
                NO_TABS,
                mUiUtils.getItemDesc(mockInstance(57, 0, 0, false)));

        // Current instance -> current
        assertEquals(
                "Current instance has a wrong description",
                CURRENT,
                mUiUtils.getItemDesc(mockInstance(InstanceInfo.Type.CURRENT)));

        // Other visible instance -> 'window is open'
        assertEquals(
                "Visible instance has a wrong description",
                OPEN,
                mUiUtils.getItemDesc(mockInstance(InstanceInfo.Type.ADJACENT)));

        // Normal tabs only -> # of tabs
        int normalTabCount = 3;
        int incognitoTabCount = 0;
        int totalTabCount = 3;
        InstanceInfo item = mockInstance(57, normalTabCount, incognitoTabCount, false);
        mUiUtils.getItemDesc(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_tab_count_nonzero,
                        item.tabCount,
                        item.tabCount);
        clearInvocations(mResources);

        // Mixed tabs -> # tabs, # incognito
        normalTabCount = 3;
        incognitoTabCount = 2;
        totalTabCount = 5;
        item = mockInstance(57, normalTabCount, incognitoTabCount, false);
        mUiUtils.getItemDesc(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_desc_mixed,
                        totalTabCount,
                        incognitoTabCount,
                        totalTabCount,
                        incognitoTabCount);
        clearInvocations(mResources);

        // Incognito-selected, incognito tab only -> # incognito tabs
        incognitoTabCount = 4;
        item = mockInstance(57, 0, incognitoTabCount, true);
        mUiUtils.getItemDesc(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_desc_incognito,
                        incognitoTabCount,
                        incognitoTabCount);
        clearInvocations(mResources);

        // Incognito-selected, mixed tabs -> # tabs, # incognito
        normalTabCount = 7;
        incognitoTabCount = 13;
        totalTabCount = 7 + 13;
        item = mockInstance(57, normalTabCount, incognitoTabCount, true);
        mUiUtils.getItemDesc(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_desc_mixed,
                        totalTabCount,
                        incognitoTabCount,
                        totalTabCount,
                        incognitoTabCount);
        clearInvocations(mResources);

        // Disregard incognito tab count for a killed task -> # tabs
        normalTabCount = 3;
        incognitoTabCount = 2;
        item = mockInstance(UiUtils.INVALID_TASK_ID, normalTabCount, incognitoTabCount, false);
        mUiUtils.getItemDesc(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_tab_count_nonzero,
                        normalTabCount,
                        normalTabCount);

        clearInvocations(mResources);

        // Incognito-selected, mixed tabs, killed task -> # tabs
        normalTabCount = 2;
        incognitoTabCount = 2;
        item = mockInstance(UiUtils.INVALID_TASK_ID, normalTabCount, incognitoTabCount, true);
        mUiUtils.getItemDesc(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_tab_count_nonzero,
                        normalTabCount,
                        normalTabCount);
    }

    @Test
    public void testCloseConfirmationMessage() {
        // Mixed tabs -> TITLE and # more tabs...
        int normalTabCount = 3;
        int incognitoTabCount = 2;
        int totalTabCount = 5;
        InstanceInfo item = mockInstance(57, normalTabCount, incognitoTabCount, false);
        mUiUtils.getConfirmationMessage(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_close_confirm_deleted_tabs_many,
                        totalTabCount - 1,
                        TITLE,
                        totalTabCount - 1,
                        TITLE);
        clearInvocations(mResources);

        // Mixed tabs, incognito-selected -> Incognito and # more tabs...
        item = mockInstance(57, normalTabCount, incognitoTabCount, true);
        mUiUtils.getConfirmationMessage(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_close_confirm_deleted_incognito_mixed,
                        normalTabCount,
                        incognitoTabCount,
                        normalTabCount,
                        incognitoTabCount);
        clearInvocations(mResources);

        // Incognito tabs only -> # incognito tabs...
        normalTabCount = 0;
        item = mockInstance(57, normalTabCount, incognitoTabCount, true);
        mUiUtils.getConfirmationMessage(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_close_confirm_deleted_incognito,
                        incognitoTabCount,
                        incognitoTabCount);
        clearInvocations(mResources);

        // Single tab -> The tab TITLE...
        normalTabCount = 1;
        incognitoTabCount = 0;
        item = mockInstance(57, normalTabCount, incognitoTabCount, false);
        mUiUtils.getConfirmationMessage(item);
        verify(mResources)
                .getString(R.string.instance_switcher_close_confirm_deleted_tabs_one, TITLE);
        clearInvocations(mResources);

        // No tab -> The window...
        normalTabCount = 0;
        incognitoTabCount = 0;
        item = mockInstance(57, normalTabCount, incognitoTabCount, false);
        mUiUtils.getConfirmationMessage(item);
        verify(mResources).getString(R.string.instance_switcher_close_confirm_deleted_tabs_zero);
        clearInvocations(mResources);

        // Incognito-selected, mixed tabs, killed task ->  TITLE and 2 more tabs...
        // Incognito tabs are not restored. Shown with the last focused normal tab info.
        normalTabCount = 3;
        incognitoTabCount = 2;
        totalTabCount = 3; // 2 incognito tabs are discarded.
        item = mockInstance(-1, normalTabCount, incognitoTabCount, true);
        mUiUtils.getConfirmationMessage(item);
        verify(mResources)
                .getQuantityString(
                        R.plurals.instance_switcher_close_confirm_deleted_tabs_many,
                        totalTabCount - 1,
                        TITLE,
                        totalTabCount - 1,
                        TITLE);
    }

    private InstanceInfo mockInstance(
            int taskId, int tabCount, int incognitoTabCount, boolean isIncognito) {
        return new InstanceInfo(
                1, taskId, 0, "https://url.com", TITLE, tabCount, incognitoTabCount, isIncognito);
    }

    private InstanceInfo mockInstance(int type) {
        return new InstanceInfo(1, 57, type, "https://url.com", TITLE, 1, 1, true);
    }

    private InstanceInfo mockInstanceBeforeLoadingTab(int type) {
        return new InstanceInfo(1, 57, type, null, null, 1, 0, false);
    }
}