chromium/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java

// Copyright 2019 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.tasks.tab_management;

import static androidx.test.espresso.matcher.ViewMatchers.assertThat;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
import static org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper.moveActivityToFront;
import static org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper.waitForSecondChromeTabbedActivity;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.leaveTabSwitcher;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.switchTabModel;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabModelTabCount;
import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabSwitcherCardCount;

import android.os.Build;

import android.os.Build.VERSION_CODES;
import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;

import org.chromium.base.test.util.DisableIf;
import org.junit.After;
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.test.util.ApplicationTestUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.layouts.LayoutType;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.chrome.test.util.MenuUtils;
import org.chromium.ui.test.util.UiRestriction;

/** Tests for Multi-window related behavior in grid tab switcher. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({
    ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
    ChromeSwitches.DISABLE_TAB_MERGING_FOR_TESTING
})
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
@DisableFeatures({ChromeFeatureList.TAB_GROUP_PARITY_ANDROID})
@DisableIf.Build(sdk_is_greater_than = VERSION_CODES.S_V2) // https://crbug.com/1297370
// TODO(crbug.com/344669867): Failing when batched, batch this again.
public class TabSwitcherMultiWindowTest {
    @ClassRule
    public static ChromeTabbedActivityTestRule sActivityTestRule =
            new ChromeTabbedActivityTestRule();

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

    private ChromeTabbedActivity mCta1;
    private ChromeTabbedActivity mCta2;

    @Before
    public void setUp() {
        TabUiTestHelper.verifyTabSwitcherLayoutType(sActivityTestRule.getActivity());
        mCta1 = sActivityTestRule.getActivity();
        CriteriaHelper.pollUiThread(mCta1.getTabModelSelector()::isTabStateInitialized);
    }

    @After
    public void tearDown() throws Exception {
        // On Android N this throws an exception because of legacy multi window.
        if (mCta2 != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            ApplicationTestUtils.finishActivity(mCta2);
        }
        if (mCta1 != null) {
            moveActivityToFront(mCta1);
            if (mCta1.getLayoutManager().isLayoutVisible(LayoutType.TAB_SWITCHER)) {
                leaveTabSwitcher(mCta1);
            }
        }
    }

    @Test
    @LargeTest
    public void testMoveTabsAcrossWindow_GTS_WithoutGroup() {
        // Initially, we have 4 normal tabs (including the one created at activity start) and 3
        // incognito tabs in mCta1.
        TabUiTestHelper.addBlankTabs(mCta1, false, 3);
        TabUiTestHelper.addBlankTabs(mCta1, true, 3);
        verifyTabModelTabCount(mCta1, 4, 3);

        // Enter tab switcher in mCta1 in incognito mode.
        enterTabSwitcher(mCta1);
        assertTrue(mCta1.getTabModelSelector().getCurrentModel().isIncognito());

        // Before move, there are 3 incognito tabs in mCta1.
        verifyTabSwitcherCardCount(mCta1, 3);

        // Move 2 incognito tabs to mCta2.
        clickFirstCardFromTabSwitcher(mCta1);
        MenuUtils.invokeCustomMenuActionSync(
                InstrumentationRegistry.getInstrumentation(),
                mCta1,
                R.id.move_to_other_window_menu_id);
        mCta2 = waitForSecondChromeTabbedActivity();
        CriteriaHelper.pollUiThread(mCta2.getTabModelSelector()::isTabStateInitialized);
        moveActivityToFront(mCta1);
        moveTabsToOtherWindow(mCta1, 1);

        // After move, there are 1 incognito tab in mCta1 and 2 incognito tabs in mCta2.
        enterTabSwitcher(mCta1);
        verifyTabSwitcherCardCount(mCta1, 1);
        clickFirstCardFromTabSwitcher(mCta1);
        moveActivityToFront(mCta2);
        enterTabSwitcher(mCta2);
        verifyTabSwitcherCardCount(mCta2, 2);
        verifyTabModelTabCount(mCta1, 4, 1);
        verifyTabModelTabCount(mCta2, 0, 2);

        // Move 1 incognito tab back to mCta1.
        clickFirstCardFromTabSwitcher(mCta2);
        moveTabsToOtherWindow(mCta2, 1);

        // After move, there are 2 incognito tabs in mCta1 and 1 incognito tab in mCta2.
        enterTabSwitcher(mCta2);
        verifyTabSwitcherCardCount(mCta2, 1);
        clickFirstCardFromTabSwitcher(mCta2);
        moveActivityToFront(mCta1);
        enterTabSwitcher(mCta1);
        verifyTabSwitcherCardCount(mCta1, 2);
        verifyTabModelTabCount(mCta1, 4, 2);
        verifyTabModelTabCount(mCta2, 0, 1);

        // Switch to normal tab list in mCta1.
        switchTabModel(mCta1, false);
        assertFalse(mCta1.getTabModelSelector().getCurrentModel().isIncognito());

        // Reset back nav so clean up is able to exit the tab switcher.
        clickFirstCardFromTabSwitcher(mCta1);
        enterTabSwitcher(mCta1);
    }

    @Test
    @MediumTest
    public void testMoveLastIncognitoTab() {
        // Initially, we have 1 normal tab (created in #setup()) and 1 incognito tab in mCta1.
        TabUiTestHelper.addBlankTabs(mCta1, true, 1);
        verifyTabModelTabCount(mCta1, 1, 1);

        // Move the incognito tab to mCta2.
        assertTrue(mCta1.getTabModelSelector().getCurrentModel().isIncognito());
        MenuUtils.invokeCustomMenuActionSync(
                InstrumentationRegistry.getInstrumentation(),
                mCta1,
                R.id.move_to_other_window_menu_id);
        mCta2 = waitForSecondChromeTabbedActivity();
        CriteriaHelper.pollUiThread(mCta2.getTabModelSelector()::isTabStateInitialized);

        assertThat(
                mCta1.getTabModelSelector()
                        .getTabModelFilterProvider()
                        .getTabModelFilter(true)
                        .getCount(),
                is(0));
        assertThat(
                mCta2.getTabModelSelector()
                        .getTabModelFilterProvider()
                        .getTabModelFilter(true)
                        .getCount(),
                is(1));
    }

    private void moveTabsToOtherWindow(ChromeTabbedActivity cta, int number) {
        for (int i = 0; i < number; i++) {
            MenuUtils.invokeCustomMenuActionSync(
                    InstrumentationRegistry.getInstrumentation(),
                    cta,
                    R.id.move_to_other_window_menu_id);
            moveActivityToFront(cta);
        }
    }
}