| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.chrome.features.start_surface; |
| |
| import static android.support.test.espresso.Espresso.onView; |
| import static android.support.test.espresso.action.ViewActions.click; |
| import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; |
| import static android.support.test.espresso.assertion.ViewAssertions.matches; |
| import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition; |
| import static android.support.test.espresso.matcher.ViewMatchers.Visibility.GONE; |
| import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; |
| import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription; |
| import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; |
| import static android.support.test.espresso.matcher.ViewMatchers.withId; |
| import static android.support.test.espresso.matcher.ViewMatchers.withParent; |
| import static android.support.test.espresso.matcher.ViewMatchers.withText; |
| |
| import static org.hamcrest.CoreMatchers.not; |
| import static org.hamcrest.core.AllOf.allOf; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.areAnimatorsEnabled; |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.closeFirstTabInTabSwitcher; |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabGroup; |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs; |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher; |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.getSwipeToDismissAction; |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.rotateDeviceToOrientation; |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabModelTabCount; |
| import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabSwitcherCardCount; |
| import static org.chromium.components.embedder_support.util.UrlConstants.NTP_URL; |
| import static org.chromium.content_public.browser.test.util.CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL; |
| import static org.chromium.content_public.browser.test.util.CriteriaHelper.DEFAULT_POLLING_INTERVAL; |
| |
| import android.content.pm.ActivityInfo; |
| import android.content.res.Configuration; |
| import android.graphics.Bitmap; |
| import android.graphics.drawable.BitmapDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.os.Build; |
| import android.support.test.InstrumentationRegistry; |
| import android.support.test.espresso.Espresso; |
| import android.support.test.espresso.NoMatchingViewException; |
| import android.support.test.espresso.ViewAssertion; |
| import android.support.test.espresso.contrib.AccessibilityChecks; |
| import android.support.test.espresso.contrib.RecyclerViewActions; |
| import android.support.test.filters.MediumTest; |
| import android.text.TextUtils; |
| import android.view.View; |
| import android.widget.ImageView; |
| |
| import androidx.annotation.Nullable; |
| import androidx.recyclerview.widget.GridLayoutManager; |
| import androidx.recyclerview.widget.RecyclerView; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.ClassRule; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TestRule; |
| import org.junit.runner.RunWith; |
| |
| import org.chromium.base.Callback; |
| import org.chromium.base.GarbageCollectionTestUtils; |
| import org.chromium.base.metrics.RecordHistogram; |
| import org.chromium.base.test.util.CommandLineFlags; |
| import org.chromium.base.test.util.DisableIf; |
| import org.chromium.base.test.util.DisabledTest; |
| import org.chromium.base.test.util.Feature; |
| import org.chromium.base.test.util.FlakyTest; |
| import org.chromium.base.test.util.MinAndroidSdkLevel; |
| import org.chromium.base.test.util.Restriction; |
| import org.chromium.chrome.browser.ChromeTabbedActivity; |
| import org.chromium.chrome.browser.compositor.layouts.Layout; |
| import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; |
| import org.chromium.chrome.browser.flags.ChromeFeatureList; |
| import org.chromium.chrome.browser.flags.ChromeSwitches; |
| import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory; |
| import org.chromium.chrome.browser.tab.Tab; |
| import org.chromium.chrome.browser.tabmodel.TabModel; |
| import org.chromium.chrome.browser.tasks.tab_management.TabProperties; |
| import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorTestingRobot; |
| import org.chromium.chrome.browser.tasks.tab_management.TabSuggestionMessageService; |
| import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher; |
| import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherCoordinator; |
| import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities; |
| import org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper; |
| import org.chromium.chrome.tab_ui.R; |
| import org.chromium.chrome.test.ChromeJUnit4ClassRunner; |
| import org.chromium.chrome.test.ChromeTabbedActivityTestRule; |
| import org.chromium.chrome.test.util.ApplicationTestUtils; |
| import org.chromium.chrome.test.util.ChromeRenderTestRule; |
| import org.chromium.chrome.test.util.ChromeTabUtils; |
| import org.chromium.chrome.test.util.MenuUtils; |
| import org.chromium.chrome.test.util.OverviewModeBehaviorWatcher; |
| import org.chromium.chrome.test.util.browser.Features; |
| import org.chromium.chrome.test.util.browser.Features.DisableFeatures; |
| import org.chromium.chrome.test.util.browser.Features.EnableFeatures; |
| import org.chromium.content_public.browser.LoadUrlParams; |
| import org.chromium.content_public.browser.test.util.Criteria; |
| import org.chromium.content_public.browser.test.util.CriteriaHelper; |
| import org.chromium.content_public.browser.test.util.TestThreadUtils; |
| import org.chromium.content_public.browser.test.util.WebContentsUtils; |
| import org.chromium.net.test.EmbeddedTestServer; |
| import org.chromium.ui.base.PageTransition; |
| import org.chromium.ui.test.util.DisableAnimationsTestRule; |
| import org.chromium.ui.test.util.UiRestriction; |
| import org.chromium.ui.widget.ChipView; |
| import org.chromium.ui.widget.ChromeImageView; |
| import org.chromium.ui.widget.ViewLookupCachingFrameLayout; |
| |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.lang.ref.WeakReference; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| // clang-format off |
| /** Tests for the {@link StartSurfaceLayout} */ |
| @RunWith(ChromeJUnit4ClassRunner.class) |
| @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, |
| "force-fieldtrials=Study/Group"}) |
| @EnableFeatures({ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID + "<Study", |
| ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study"}) |
| @Restriction( |
| {UiRestriction.RESTRICTION_TYPE_PHONE, Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE}) |
| public class StartSurfaceLayoutTest { |
| // clang-format on |
| private static final String BASE_PARAMS = "force-fieldtrial-params=" |
| + "Study.Group:soft-cleanup-delay/0/cleanup-delay/0/skip-slow-zooming/false" |
| + "/zooming-min-sdk-version/19/zooming-min-memory-mb/512"; |
| |
| // Tests need animation on. |
| @ClassRule |
| public static DisableAnimationsTestRule sEnableAnimationsRule = |
| new DisableAnimationsTestRule(true); |
| |
| @Rule |
| public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); |
| |
| @Rule |
| public TestRule mProcessor = new Features.InstrumentationProcessor(); |
| |
| @Rule |
| public ChromeRenderTestRule mRenderTestRule = new ChromeRenderTestRule(); |
| |
| private StartSurfaceLayout mStartSurfaceLayout; |
| private String mUrl; |
| private int mRepeat; |
| private List<WeakReference<Bitmap>> mAllBitmaps = new LinkedList<>(); |
| private Callback<Bitmap> mBitmapListener = |
| (bitmap) -> mAllBitmaps.add(new WeakReference<>(bitmap)); |
| private TabSwitcher.TabListDelegate mTabListDelegate; |
| |
| @Before |
| public void setUp() { |
| AccessibilityChecks.enable(); |
| EmbeddedTestServer testServer = |
| EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext()); |
| // After setUp, Chrome is launched and has one NTP. |
| mActivityTestRule.startMainActivityFromLauncher(); |
| |
| Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout(); |
| assertTrue(layout instanceof StartSurfaceLayout); |
| mStartSurfaceLayout = (StartSurfaceLayout) layout; |
| mUrl = testServer.getURL("/chrome/test/data/android/navigate/simple.html"); |
| mRepeat = 1; |
| |
| mTabListDelegate = mStartSurfaceLayout.getStartSurfaceForTesting().getTabListDelegate(); |
| mTabListDelegate.setBitmapCallbackForTesting(mBitmapListener); |
| assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting()); |
| |
| mActivityTestRule.getActivity().getTabContentManager().setCaptureMinRequestTimeForTesting( |
| 0); |
| |
| CriteriaHelper.pollUiThread(Criteria.equals(true, |
| mActivityTestRule.getActivity() |
| .getTabModelSelector() |
| .getTabModelFilterProvider() |
| .getCurrentTabModelFilter()::isTabModelRestored)); |
| |
| assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting()); |
| } |
| |
| @After |
| public void tearDown() { |
| mActivityTestRule.getActivity().setRequestedOrientation( |
| ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"RenderTest"}) |
| // clang-format off |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testRenderGrid_3WebTabs() throws InterruptedException, IOException { |
| // clang-format on |
| prepareTabs(3, 0, "about:blank"); |
| ChromeTabUtils.switchTabInCurrentTabModel(mActivityTestRule.getActivity(), 0); |
| enterGTSWithThumbnailChecking(); |
| // See crbug.com/1063619 |
| mRenderTestRule.setPixelDiffThreshold(2); |
| mRenderTestRule.render( |
| mActivityTestRule.getActivity().findViewById(R.id.tab_list_view), "3_web_tabs"); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"RenderTest"}) |
| // clang-format off |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testRenderGrid_10WebTabs() throws InterruptedException, IOException { |
| // clang-format on |
| prepareTabs(10, 0, "about:blank"); |
| ChromeTabUtils.switchTabInCurrentTabModel(mActivityTestRule.getActivity(), 0); |
| enterGTSWithThumbnailChecking(); |
| // See crbug.com/1063619 |
| mRenderTestRule.setPixelDiffThreshold(2); |
| mRenderTestRule.render( |
| mActivityTestRule.getActivity().findViewById(R.id.tab_list_view), "10_web_tabs"); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"RenderTest"}) |
| // clang-format off |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testRenderGrid_10WebTabs_InitialScroll() throws InterruptedException, IOException { |
| // clang-format on |
| prepareTabs(10, 0, "about:blank"); |
| ChromeTabUtils.switchTabInCurrentTabModel(mActivityTestRule.getActivity(), 9); |
| enterGTSWithThumbnailChecking(); |
| // See crbug.com/1063619 |
| mRenderTestRule.setPixelDiffThreshold(2); |
| // Make sure the grid tab switcher is scrolled down to show the selected tab. |
| mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(R.id.tab_list_view), |
| "10_web_tabs-select_last"); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"RenderTest"}) |
| // clang-format off |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testRenderGrid_Incognito() throws InterruptedException, IOException { |
| // clang-format on |
| // Prepare some incognito tabs and enter tab switcher. |
| prepareTabs(1, 3, "about:blank"); |
| assertTrue(mActivityTestRule.getActivity().getCurrentTabModel().isIncognito()); |
| ChromeTabUtils.switchTabInCurrentTabModel(mActivityTestRule.getActivity(), 0); |
| enterGTSWithThumbnailChecking(); |
| // See crbug.com/1063619 |
| mRenderTestRule.setPixelDiffThreshold(2); |
| mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(R.id.tab_list_view), |
| "3_incognito_web_tabs"); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"RenderTest"}) |
| // clang-format off |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @FlakyTest(message = "crbug.com/1064157 This test is flaky") |
| public void testRenderGrid_3NativeTabs() throws InterruptedException, IOException { |
| // clang-format on |
| assertTrue(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| // Prepare some incognito native tabs and enter tab switcher. |
| // NTP in incognito mode is chosen for its consistency in look, and we don't have to mock |
| // away the MV tiles, login promo, feed, etc. |
| prepareTabs(1, 3, null); |
| assertTrue(mActivityTestRule.getActivity().getCurrentTabModel().isIncognito()); |
| ChromeTabUtils.switchTabInCurrentTabModel(mActivityTestRule.getActivity(), 0); |
| enterGTSWithThumbnailChecking(); |
| // See crbug.com/1063620 |
| mRenderTestRule.setPixelDiffThreshold(3); |
| mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(R.id.tab_list_view), |
| "3_incognito_ntps"); |
| } |
| |
| @Test |
| @MediumTest |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testTabToGridFromLiveTab() throws InterruptedException { |
| assertFalse(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| assertEquals(0, mTabListDelegate.getSoftCleanupDelayForTesting()); |
| assertEquals(0, mTabListDelegate.getCleanupDelayForTesting()); |
| |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(mUrl); |
| } |
| |
| @Test |
| @MediumTest |
| @EnableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP) |
| @DisabledTest(message = "crbug.com/991852 This test is flaky") |
| public void testTabToGridFromLiveTabAnimation() throws InterruptedException { |
| assertTrue(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(mUrl); |
| } |
| |
| @Test |
| @MediumTest |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/soft-cleanup-delay/2000/cleanup-delay/10000"}) |
| public void testTabToGridFromLiveTabWarm() throws InterruptedException { |
| assertFalse(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| assertEquals(2000, mTabListDelegate.getSoftCleanupDelayForTesting()); |
| assertEquals(10000, mTabListDelegate.getCleanupDelayForTesting()); |
| |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(mUrl); |
| } |
| |
| @Test |
| @MediumTest |
| @EnableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/soft-cleanup-delay/2000/cleanup-delay/10000"}) |
| @MinAndroidSdkLevel(Build.VERSION_CODES.M) // TODO(crbug.com/997065#c8): remove SDK restriction. |
| public void testTabToGridFromLiveTabWarmAnimation() throws InterruptedException { |
| assertTrue(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(mUrl); |
| } |
| |
| @Test |
| @MediumTest |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/cleanup-delay/10000"}) |
| public void testTabToGridFromLiveTabSoft() throws InterruptedException { |
| assertFalse(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| assertEquals(0, mTabListDelegate.getSoftCleanupDelayForTesting()); |
| assertEquals(10000, mTabListDelegate.getCleanupDelayForTesting()); |
| |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(mUrl); |
| } |
| |
| @Test |
| @MediumTest |
| @EnableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/cleanup-delay/10000"}) |
| @MinAndroidSdkLevel(Build.VERSION_CODES.M) // TODO(crbug.com/997065#c8): remove SDK restriction. |
| public void testTabToGridFromLiveTabSoftAnimation() throws InterruptedException { |
| assertTrue(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(mUrl); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testTabToGridFromNtp() throws InterruptedException { |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(NTP_URL); |
| } |
| |
| /** |
| * Make Chrome have {@code numTabs} of regular Tabs and {@code numIncognitoTabs} of incognito |
| * tabs with {@code url} loaded, and assert no bitmap fetching occurred. |
| * |
| * @param numTabs The number of regular tabs. |
| * @param numIncognitoTabs The number of incognito tabs. |
| * @param url The URL to load. |
| */ |
| private void prepareTabs(int numTabs, int numIncognitoTabs, @Nullable String url) { |
| int oldCount = mTabListDelegate.getBitmapFetchCountForTesting(); |
| TabUiTestHelper.prepareTabsWithThumbnail(mActivityTestRule, numTabs, numIncognitoTabs, url); |
| assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting() - oldCount); |
| } |
| |
| private void testTabToGrid(String fromUrl) throws InterruptedException { |
| mActivityTestRule.loadUrl(fromUrl); |
| |
| final int initCount = getCaptureCount(); |
| |
| for (int i = 0; i < mRepeat; i++) { |
| enterGTSWithThumbnailChecking(); |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| } |
| checkFinalCaptureCount(false, initCount); |
| } |
| |
| @Test |
| @MediumTest |
| public void testGridToTabToCurrentNTP() throws InterruptedException { |
| prepareTabs(1, 0, NTP_URL); |
| testGridToTab(false, false); |
| } |
| |
| @Test |
| @MediumTest |
| public void testGridToTabToOtherNTP() throws InterruptedException { |
| prepareTabs(2, 0, NTP_URL); |
| testGridToTab(true, false); |
| } |
| |
| @Test |
| @MediumTest |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| public void testGridToTabToCurrentLive() throws InterruptedException { |
| assertFalse(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| prepareTabs(1, 0, mUrl); |
| testGridToTab(false, false); |
| } |
| |
| // From https://stackoverflow.com/a/21505193 |
| private static boolean isEmulator() { |
| return Build.FINGERPRINT.startsWith("generic") || Build.FINGERPRINT.startsWith("unknown") |
| || Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator") |
| || Build.MODEL.contains("Android SDK built for x86") |
| || Build.MANUFACTURER.contains("Genymotion") |
| || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) |
| || "google_sdk".equals(Build.PRODUCT); |
| } |
| |
| /** |
| * Test that even if there are tabs with stuck pending thumbnail readback, it doesn't block |
| * thumbnail readback for the current tab. |
| */ |
| @Test |
| @MediumTest |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| public void testGridToTabToCurrentLiveDetached() throws Exception { |
| assertFalse(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| // This works on emulators but not on real devices. See crbug.com/986047. |
| if (!isEmulator()) return; |
| |
| for (int i = 0; i < 10; i++) { |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| // Quickly create some tabs, navigate to web pages, and don't wait for thumbnail |
| // capturing. |
| mActivityTestRule.loadUrl(mUrl); |
| ChromeTabUtils.newTabFromMenu( |
| InstrumentationRegistry.getInstrumentation(), cta, false, false); |
| mActivityTestRule.loadUrl(mUrl); |
| // Hopefully we are in a state where some pending readbacks are stuck because their tab |
| // is not attached to the view. |
| if (cta.getTabContentManager().getPendingReadbacksForTesting() > 0) { |
| break; |
| } |
| |
| // Restart Chrome. |
| // Although we're destroying the activity, the Application will still live on since its |
| // in the same process as this test. |
| TestThreadUtils.runOnUiThreadBlocking(() -> cta.getTabModelSelector().closeAllTabs()); |
| ApplicationTestUtils.finishActivity(cta); |
| mActivityTestRule.startMainActivityOnBlankPage(); |
| assertEquals(1, mActivityTestRule.tabsCount(false)); |
| } |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| assertNotEquals(0, cta.getTabContentManager().getPendingReadbacksForTesting()); |
| assertEquals(1, cta.getCurrentTabModel().index()); |
| |
| // The last tab should still get thumbnail even though readbacks for other tabs are stuck. |
| enterTabSwitcher(cta); |
| TabUiTestHelper.checkThumbnailsExist(cta.getTabModelSelector().getCurrentTab()); |
| } |
| |
| @Test |
| @MediumTest |
| @EnableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP) |
| @DisabledTest(message = "crbug.com/993201 This test fails deterministically on Nexus 5X") |
| public void testGridToTabToCurrentLiveWithAnimation() throws InterruptedException { |
| assertTrue(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| prepareTabs(1, 0, mUrl); |
| testGridToTab(false, false); |
| } |
| |
| @Test |
| @MediumTest |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| public void testGridToTabToOtherLive() throws InterruptedException { |
| assertFalse(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| prepareTabs(2, 0, mUrl); |
| testGridToTab(true, false); |
| } |
| |
| @Test |
| @MediumTest |
| @EnableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP) |
| @DisabledTest(message = "crbug.com/993201 This test fails deterministically on Nexus 5X") |
| public void testGridToTabToOtherLiveWithAnimation() throws InterruptedException { |
| assertTrue(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| prepareTabs(2, 0, mUrl); |
| testGridToTab(true, false); |
| } |
| |
| @Test |
| @MediumTest |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| public void testGridToTabToOtherFrozen() throws InterruptedException { |
| assertFalse(TabUiFeatureUtilities.isTabToGtsAnimationEnabled()); |
| prepareTabs(2, 0, mUrl); |
| testGridToTab(true, true); |
| } |
| |
| private void testGridToTab(boolean switchToAnotherTab, boolean killBeforeSwitching) |
| throws InterruptedException { |
| final int initCount = getCaptureCount(); |
| |
| for (int i = 0; i < mRepeat; i++) { |
| enterGTSWithThumbnailChecking(); |
| |
| final int index = mActivityTestRule.getActivity().getCurrentTabModel().index(); |
| final int targetIndex = switchToAnotherTab ? 1 - index : index; |
| Tab targetTab = |
| mActivityTestRule.getActivity().getCurrentTabModel().getTabAt(targetIndex); |
| if (killBeforeSwitching) { |
| WebContentsUtils.simulateRendererKilled(targetTab.getWebContents(), false); |
| } |
| |
| if (switchToAnotherTab) { |
| waitForCaptureRateControl(); |
| } |
| int count = getCaptureCount(); |
| onView(withId(R.id.tab_list_view)) |
| .perform(actionOnItemAtPosition(targetIndex, click())); |
| CriteriaHelper.pollUiThread(() -> { |
| boolean doneHiding = |
| !mActivityTestRule.getActivity().getLayoutManager().overviewVisible(); |
| if (!doneHiding) { |
| // Before overview hiding animation is done, the tab index should not change. |
| assertEquals( |
| index, mActivityTestRule.getActivity().getCurrentTabModel().index()); |
| } |
| return doneHiding; |
| }, "Overview not hidden yet"); |
| int delta; |
| if (switchToAnotherTab |
| && !TextUtils.equals(mActivityTestRule.getActivity() |
| .getCurrentWebContents() |
| .getLastCommittedUrl(), |
| NTP_URL)) { |
| // Capture the original tab. |
| delta = 1; |
| } else { |
| delta = 0; |
| } |
| checkCaptureCount(delta, count); |
| } |
| checkFinalCaptureCount(switchToAnotherTab, initCount); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testRestoredTabsDontFetch() throws Exception { |
| prepareTabs(2, 0, mUrl); |
| int oldCount = mTabListDelegate.getBitmapFetchCountForTesting(); |
| |
| // Restart Chrome. |
| // Although we're destroying the activity, the Application will still live on since its in |
| // the same process as this test. |
| ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity()); |
| mActivityTestRule.startMainActivityOnBlankPage(); |
| assertEquals(3, mActivityTestRule.tabsCount(false)); |
| |
| Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout(); |
| assertTrue(layout instanceof StartSurfaceLayout); |
| mStartSurfaceLayout = (StartSurfaceLayout) layout; |
| assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting() - oldCount); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testInvisibleTabsDontFetch() throws InterruptedException { |
| // Open a few new tabs. |
| final int count = mTabListDelegate.getBitmapFetchCountForTesting(); |
| for (int i = 0; i < 3; i++) { |
| MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(), |
| mActivityTestRule.getActivity(), org.chromium.chrome.R.id.new_tab_menu_id); |
| } |
| // Fetching might not happen instantly. |
| Thread.sleep(1000); |
| |
| // No fetching should happen. |
| assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting() - count); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS + "/soft-cleanup-delay/2000/cleanup-delay/10000"}) |
| public void testInvisibleTabsDontFetchWarm() throws InterruptedException { |
| // Get the GTS in the warm state. |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(NTP_URL); |
| |
| Thread.sleep(1000); |
| |
| // Open a few new tabs. |
| final int count = mTabListDelegate.getBitmapFetchCountForTesting(); |
| for (int i = 0; i < 3; i++) { |
| MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(), |
| mActivityTestRule.getActivity(), org.chromium.chrome.R.id.new_tab_menu_id); |
| } |
| // Fetching might not happen instantly. |
| Thread.sleep(1000); |
| |
| // No fetching should happen. |
| assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting() - count); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS + "/cleanup-delay/10000"}) |
| public void testInvisibleTabsDontFetchSoft() throws InterruptedException { |
| // Get the GTS in the soft cleaned up state. |
| prepareTabs(2, 0, NTP_URL); |
| testTabToGrid(NTP_URL); |
| |
| Thread.sleep(1000); |
| |
| // Open a few new tabs. |
| final int count = mTabListDelegate.getBitmapFetchCountForTesting(); |
| for (int i = 0; i < 3; i++) { |
| MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(), |
| mActivityTestRule.getActivity(), org.chromium.chrome.R.id.new_tab_menu_id); |
| } |
| // Fetching might not happen instantly. |
| Thread.sleep(1000); |
| |
| // No fetching should happen. |
| assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting() - count); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @DisabledTest(message = "http://crbug/1005865 - Test was previously flaky but only on bots." |
| + "Was not locally reproducible. Disabling until verified that it's deflaked on bots.") |
| public void testIncognitoEnterGts() throws InterruptedException { |
| prepareTabs(1, 1, null); |
| enterGTSWithThumbnailChecking(); |
| onView(withId(R.id.tab_list_view)) |
| .check(TabCountAssertion.havingTabCount(1)); |
| |
| onView(withId(R.id.tab_list_view)).perform(actionOnItemAtPosition(0, click())); |
| CriteriaHelper.pollUiThread( |
| () -> !mActivityTestRule.getActivity().getLayoutManager().overviewVisible()); |
| |
| enterGTSWithThumbnailChecking(); |
| onView(withId(R.id.tab_list_view)) |
| .check(TabCountAssertion.havingTabCount(1)); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M, |
| message = "https://crbug.com/1023833") |
| public void testIncognitoToggle_tabCount() throws InterruptedException { |
| mActivityTestRule.loadUrl(mUrl); |
| |
| // Prepare two incognito tabs and enter tab switcher. |
| prepareTabs(1, 2, mUrl); |
| enterGTSWithThumbnailChecking(); |
| onView(withId(R.id.tab_list_view)) |
| .check(TabCountAssertion.havingTabCount(2)); |
| |
| for (int i = 0; i < mRepeat; i++) { |
| switchTabModel(false); |
| onView(withId(R.id.tab_list_view)) |
| .check(TabCountAssertion.havingTabCount(1)); |
| |
| switchTabModel(true); |
| onView(withId(R.id.tab_list_view)) |
| .check(TabCountAssertion.havingTabCount(2)); |
| } |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M, |
| message = "https://crbug.com/1023833") |
| public void testIncognitoToggle_thumbnailFetchCount() throws InterruptedException { |
| mActivityTestRule.loadUrl(mUrl); |
| int oldFetchCount = mTabListDelegate.getBitmapFetchCountForTesting(); |
| |
| // Prepare two incognito tabs and enter tab switcher. |
| prepareTabs(1, 2, mUrl); |
| enterGTSWithThumbnailChecking(); |
| |
| int currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting(); |
| assertEquals(2, currentFetchCount - oldFetchCount); |
| oldFetchCount = currentFetchCount; |
| int oldHistogramRecord = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG); |
| |
| for (int i = 0; i < mRepeat; i++) { |
| switchTabModel(false); |
| currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting(); |
| int currentHistogramRecord = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG); |
| assertEquals(1, currentFetchCount - oldFetchCount); |
| assertEquals(1, currentHistogramRecord - oldHistogramRecord); |
| oldFetchCount = currentFetchCount; |
| oldHistogramRecord = currentHistogramRecord; |
| |
| switchTabModel(true); |
| currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting(); |
| currentHistogramRecord = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG); |
| assertEquals(2, currentFetchCount - oldFetchCount); |
| assertEquals(2, currentHistogramRecord - oldHistogramRecord); |
| oldFetchCount = currentFetchCount; |
| oldHistogramRecord = currentHistogramRecord; |
| } |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| } |
| |
| @Test |
| @MediumTest |
| // clang-format off |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID, |
| ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID}) |
| public void testUrlUpdatedNotCrashing_ForUndoableClosedTab() throws Exception { |
| // clang-format on |
| mActivityTestRule.getActivity().getSnackbarManager().disableForTesting(); |
| prepareTabs(2, 0, null); |
| enterGTSWithThumbnailChecking(); |
| |
| Tab tab = mActivityTestRule.getActivity().getTabModelSelector().getCurrentTab(); |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel().closeTab( |
| tab, false, false, true); |
| }); |
| mActivityTestRule.loadUrlInTab( |
| mUrl, PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR, tab); |
| } |
| |
| @Test |
| @MediumTest |
| // clang-format off |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID, |
| ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID}) |
| public void testUrlUpdatedNotCrashing_ForTabNotInCurrentModel() throws Exception { |
| // clang-format on |
| prepareTabs(1, 1, null); |
| enterGTSWithThumbnailChecking(); |
| |
| Tab tab = mActivityTestRule.getActivity().getTabModelSelector().getCurrentTab(); |
| switchTabModel(false); |
| |
| mActivityTestRule.loadUrlInTab( |
| mUrl, PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR, tab); |
| } |
| |
| private int getTabCountInCurrentTabModel() { |
| return mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel().getCount(); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("TabSuggestion") |
| @EnableFeatures(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true"}) |
| public void testTabSuggestionMessageCard_dismiss() throws InterruptedException { |
| prepareTabs(3, 0, null); |
| |
| // TODO(meiliang): Avoid using static variable for tracking state, |
| // TabSuggestionMessageService.isSuggestionAvailableForTesting(). Instead, we can add a |
| // dummy MessageObserver to track the availability of the suggestions. |
| CriteriaHelper.pollUiThread(TabSuggestionMessageService::isSuggestionAvailableForTesting); |
| CriteriaHelper.pollUiThread(Criteria.equals(3, this::getTabCountInCurrentTabModel)); |
| |
| enterGTSWithThumbnailChecking(); |
| |
| // TODO(meiliang): Avoid using static variable for tracking state, |
| // TabSwitcherCoordinator::hasAppendedMessagesForTesting. Instead, we can query the number |
| // of items that the inner model of the TabSwitcher has. |
| CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting); |
| onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); |
| onView(allOf(withId(R.id.close_button), withParent(withId(R.id.tab_grid_message_item)))) |
| .perform(click()); |
| onView(withId(R.id.tab_grid_message_item)).check(doesNotExist()); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("TabSuggestion") |
| @EnableFeatures(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true"}) |
| public void testTabSuggestionMessageCard_review() throws InterruptedException { |
| prepareTabs(3, 0, null); |
| |
| CriteriaHelper.pollUiThread(TabSuggestionMessageService::isSuggestionAvailableForTesting); |
| CriteriaHelper.pollUiThread(Criteria.equals(3, this::getTabCountInCurrentTabModel)); |
| |
| enterGTSWithThumbnailChecking(); |
| |
| CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting); |
| onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); |
| onView(allOf(withId(R.id.action_button), withParent(withId(R.id.tab_grid_message_item)))) |
| .perform(click()); |
| |
| TabSelectionEditorTestingRobot tabSelectionEditorTestingRobot = |
| new TabSelectionEditorTestingRobot(); |
| tabSelectionEditorTestingRobot.resultRobot.verifyTabSelectionEditorIsVisible(); |
| |
| Espresso.pressBack(); |
| tabSelectionEditorTestingRobot.resultRobot.verifyTabSelectionEditorIsHidden(); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("TabSuggestion") |
| @EnableFeatures({ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study"}) |
| @CommandLineFlags.Add({BASE_PARAMS + "/cleanup-delay/10000/baseline_tab_suggestions/true"}) |
| public void testShowOnlyOneTabSuggestionMessageCard_withSoftCleanup() |
| throws InterruptedException { |
| verifyOnlyOneTabSuggestionMessageCardIsShowing(); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("TabSuggestion") |
| @EnableFeatures({ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study"}) |
| @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true"}) |
| public void testShowOnlyOneTabSuggestionMessageCard_withHardCleanup() |
| throws InterruptedException { |
| verifyOnlyOneTabSuggestionMessageCardIsShowing(); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("TabSuggestion") |
| @EnableFeatures({ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study"}) |
| @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true"}) |
| public void testTabSuggestionMessageCardDismissAfterTabClosing() throws InterruptedException { |
| prepareTabs(3, 0, mUrl); |
| CriteriaHelper.pollUiThread(TabSuggestionMessageService::isSuggestionAvailableForTesting); |
| CriteriaHelper.pollUiThread(Criteria.equals(3, this::getTabCountInCurrentTabModel)); |
| |
| enterGTSWithThumbnailChecking(); |
| CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting); |
| onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); |
| |
| closeFirstTabInTabSwitcher(); |
| |
| CriteriaHelper.pollUiThread( |
| () -> !TabSuggestionMessageService.isSuggestionAvailableForTesting()); |
| CriteriaHelper.pollUiThread(Criteria.equals(2, this::getTabCountInCurrentTabModel)); |
| |
| onView(withId(R.id.tab_list_view)) |
| .check(TabUiTestHelper.ChildrenCountAssertion.havingTabSuggestionMessageCardCount( |
| 0)); |
| onView(withId(R.id.tab_grid_message_item)).check(doesNotExist()); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("NewTabTile") |
| // clang-format off |
| @DisableFeatures({ChromeFeatureList.CLOSE_TAB_SUGGESTIONS}) |
| @CommandLineFlags.Add({BASE_PARAMS + "/tab_grid_layout_android_new_tab_tile/NewTabTile" |
| + "/tab_grid_layout_android_new_tab/false"}) |
| public void testNewTabTile() throws InterruptedException { |
| // clang-format on |
| // TODO(yuezhanggg): Modify TabUiTestHelper.verifyTabSwitcherCardCount so that it can be |
| // used here to verify card count. Right now it doesn't work because when switching between |
| // normal/incognito, the tab list fading-in animation has not finished when check happens. |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| prepareTabs(2, 0, null); |
| |
| // New tab tile should be showing. |
| enterGTSWithThumbnailChecking(); |
| onView(withId(R.id.new_tab_tile)).check(matches(isDisplayed())); |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(3)); |
| verifyTabModelTabCount(cta, 2, 0); |
| |
| // Clicking new tab tile in normal mode should create a normal tab. |
| onView(withId(R.id.new_tab_tile)).perform(click()); |
| CriteriaHelper.pollUiThread(() -> !cta.getOverviewModeBehavior().overviewVisible()); |
| enterGTSWithThumbnailChecking(); |
| onView(withId(R.id.new_tab_tile)).check(matches(isDisplayed())); |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(4)); |
| verifyTabModelTabCount(cta, 3, 0); |
| |
| // New tab tile should be showing in incognito mode. |
| switchTabModel(true); |
| onView(withId(R.id.new_tab_tile)).check(matches(isDisplayed())); |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| |
| // Clicking new tab tile in incognito mode should create an incognito tab. |
| onView(withId(R.id.new_tab_tile)).perform(click()); |
| CriteriaHelper.pollUiThread(() -> !cta.getOverviewModeBehavior().overviewVisible()); |
| enterGTSWithThumbnailChecking(); |
| onView(withId(R.id.new_tab_tile)).check(matches(isDisplayed())); |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(2)); |
| verifyTabModelTabCount(cta, 3, 1); |
| |
| // Close all normal tabs and incognito tabs, the new tab tile should still show in both |
| // modes. |
| switchTabModel(false); |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(4)); |
| MenuUtils.invokeCustomMenuActionSync( |
| InstrumentationRegistry.getInstrumentation(), cta, R.id.close_all_tabs_menu_id); |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(withId(R.id.new_tab_tile)).check(matches(isDisplayed())); |
| switchTabModel(true); |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(withId(R.id.new_tab_tile)).check(matches(isDisplayed())); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("TabSuggestion") |
| @EnableFeatures(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/baseline_tab_suggestions/true"}) |
| public void testTabSuggestionMessageCard_orientation() throws InterruptedException { |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| prepareTabs(3, 0, null); |
| |
| CriteriaHelper.pollUiThread(TabSuggestionMessageService::isSuggestionAvailableForTesting); |
| CriteriaHelper.pollUiThread(Criteria.equals(3, this::getTabCountInCurrentTabModel)); |
| |
| enterGTSWithThumbnailChecking(); |
| CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting); |
| |
| // Force portrait mode since the device can be wrongly in landscape. See crbug/1063639. |
| rotateDeviceToOrientation(cta, Configuration.ORIENTATION_PORTRAIT); |
| |
| onView(withId(R.id.tab_list_view)) |
| .check(MessageCardWidthAssertion.checkMessageItemSpanSize(3, 2)); |
| |
| rotateDeviceToOrientation(cta, Configuration.ORIENTATION_LANDSCAPE); |
| |
| onView(withId(R.id.tab_list_view)) |
| .check(MessageCardWidthAssertion.checkMessageItemSpanSize(3, 3)); |
| } |
| |
| private static class MessageCardWidthAssertion implements ViewAssertion { |
| private int mIndex; |
| private int mSpanCount; |
| |
| public static MessageCardWidthAssertion checkMessageItemSpanSize(int index, int spanCount) { |
| return new MessageCardWidthAssertion(index, spanCount); |
| } |
| |
| public MessageCardWidthAssertion(int index, int spanCount) { |
| mIndex = index; |
| mSpanCount = spanCount; |
| } |
| |
| @Override |
| public void check(View view, NoMatchingViewException noMatchException) { |
| if (noMatchException != null) throw noMatchException; |
| int tabListPadding = |
| (int) view.getResources().getDimension(R.dimen.tab_list_card_padding); |
| |
| assertTrue(view instanceof RecyclerView); |
| RecyclerView recyclerView = (RecyclerView) view; |
| GridLayoutManager layoutManager = (GridLayoutManager) recyclerView.getLayoutManager(); |
| assertEquals(mSpanCount, layoutManager.getSpanCount()); |
| |
| RecyclerView.ViewHolder messageItemViewHolder = |
| recyclerView.findViewHolderForAdapterPosition(mIndex); |
| assertNotNull(messageItemViewHolder); |
| assertEquals(TabProperties.UiType.MESSAGE, messageItemViewHolder.getItemViewType()); |
| View messageItemView = messageItemViewHolder.itemView; |
| |
| // The message card item width should always be recyclerView width minus padding. |
| assertEquals(recyclerView.getWidth() - 2 * tabListPadding, messageItemView.getWidth()); |
| } |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("NewTabTile") |
| // clang-format off |
| @DisableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study", |
| ChromeFeatureList.CLOSE_TAB_SUGGESTIONS}) |
| @CommandLineFlags.Add({BASE_PARAMS + "/tab_grid_layout_android_new_tab_tile/false" |
| + "/tab_grid_layout_android_new_tab/false"}) |
| public void testNewTabTile_Disabled() throws InterruptedException { |
| // clang-format on |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| prepareTabs(2, 0, null); |
| |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.new_tab_tile)).check(doesNotExist()); |
| verifyTabSwitcherCardCount(cta, 2); |
| |
| switchTabModel(true); |
| onView(withId(R.id.new_tab_tile)).check(doesNotExist()); |
| verifyTabSwitcherCardCount(cta, 0); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testThumbnailAspectRatio_default() throws Exception { |
| prepareTabs(2, 0, mUrl); |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| onView(withId(R.id.tab_list_view)) |
| .check(ThumbnailAspectRatioAssertion.havingAspectRatio(1.0)); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/0.75"}) |
| public void testThumbnailAspectRatio_point75() throws Exception { |
| prepareTabs(2, 0, mUrl); |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| onView(withId(R.id.tab_list_view)) |
| .check(ThumbnailAspectRatioAssertion.havingAspectRatio(0.75)); |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| |
| Tab tab = mActivityTestRule.getActivity().getTabModelSelector().getCurrentTab(); |
| mActivityTestRule.loadUrlInTab( |
| NTP_URL, PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR, tab); |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| onView(withId(R.id.tab_list_view)) |
| .check(ThumbnailAspectRatioAssertion.havingAspectRatio(0.75)); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/2.0/allow_to_refetch/true"}) |
| public void testThumbnailAspectRatio_fromTwoToPoint75() throws Exception { |
| prepareTabs(2, 0, mUrl); |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| onView(withId(R.id.tab_list_view)) |
| .check(ThumbnailAspectRatioAssertion.havingAspectRatio(2.0)); |
| TabModel currentTabModel = |
| mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel(); |
| for (int i = 0; i < currentTabModel.getCount(); i++) { |
| TabUiTestHelper.checkThumbnailsExist(currentTabModel.getTabAt(i)); |
| } |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| |
| simulateAspectRatioChangedToPoint75(); |
| verifyAllThumbnailHasAspectRatio(0.75); |
| |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| onView(withId(R.id.tab_list_view)) |
| .check(ThumbnailAspectRatioAssertion.havingAspectRatio(2.0)); |
| ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity()); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testThumbnailFetchingResult_defaultAspectRatio() throws Exception { |
| prepareTabs(2, 0, mUrl); |
| int oldJpegCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG); |
| int oldEtc1Count = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_ETC1); |
| int oldNothingCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_NOTHING); |
| int oldDifferentAspectRatioJpegCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG); |
| |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| int currentJpegCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG); |
| int currentEtc1Count = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_ETC1); |
| int currentNothingCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_NOTHING); |
| int currentDifferentAspectRatioJpegCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG); |
| |
| assertEquals(1, currentJpegCount - oldJpegCount); |
| assertEquals(0, currentEtc1Count - oldEtc1Count); |
| assertEquals(0, currentDifferentAspectRatioJpegCount - oldDifferentAspectRatioJpegCount); |
| // Get thumbnail from a live layer. |
| assertEquals(1, currentNothingCount - oldNothingCount); |
| |
| oldJpegCount = currentJpegCount; |
| oldEtc1Count = currentEtc1Count; |
| oldNothingCount = currentNothingCount; |
| oldDifferentAspectRatioJpegCount = currentDifferentAspectRatioJpegCount; |
| |
| TabModel currentTabModel = |
| mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel(); |
| for (int i = 0; i < currentTabModel.getCount(); i++) { |
| TabUiTestHelper.checkThumbnailsExist(currentTabModel.getTabAt(i)); |
| } |
| |
| ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity()); |
| mActivityTestRule.startMainActivityFromLauncher(); |
| |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| assertEquals(2, |
| RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG) |
| - oldJpegCount); |
| assertEquals(0, |
| RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_ETC1) |
| - oldEtc1Count); |
| assertEquals(0, |
| RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_NOTHING) |
| - oldNothingCount); |
| assertEquals(0, |
| RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG) |
| - oldDifferentAspectRatioJpegCount); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/2.0/allow_to_refetch/true"}) |
| public void testThumbnailFetchingResult_changingAspectRatio() throws Exception { |
| prepareTabs(2, 0, mUrl); |
| int oldJpegCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG); |
| int oldEtc1Count = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_ETC1); |
| int oldNothingCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_NOTHING); |
| int oldDifferentAspectRatioJpegCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG); |
| |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| int currentJpegCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG); |
| int currentEtc1Count = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_ETC1); |
| int currentNothingCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_NOTHING); |
| int currentDifferentAspectRatioJpegCount = RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG); |
| |
| assertEquals(1, currentJpegCount - oldJpegCount); |
| assertEquals(0, currentEtc1Count - oldEtc1Count); |
| assertEquals(0, currentDifferentAspectRatioJpegCount - oldDifferentAspectRatioJpegCount); |
| // Get thumbnail from a live layer. |
| assertEquals(1, currentNothingCount - oldNothingCount); |
| |
| oldJpegCount = currentJpegCount; |
| oldEtc1Count = currentEtc1Count; |
| oldNothingCount = currentNothingCount; |
| oldDifferentAspectRatioJpegCount = currentDifferentAspectRatioJpegCount; |
| |
| onView(withId(R.id.tab_list_view)) |
| .check(ThumbnailAspectRatioAssertion.havingAspectRatio(2.0)); |
| |
| TabModel currentTabModel = |
| mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel(); |
| for (int i = 0; i < currentTabModel.getCount(); i++) { |
| TabUiTestHelper.checkThumbnailsExist(currentTabModel.getTabAt(i)); |
| } |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| |
| simulateAspectRatioChangedToPoint75(); |
| verifyAllThumbnailHasAspectRatio(0.75); |
| |
| enterTabSwitcher(mActivityTestRule.getActivity()); |
| assertEquals(0, |
| RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_JPEG) |
| - oldJpegCount); |
| assertEquals(2, |
| RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG) |
| - oldDifferentAspectRatioJpegCount); |
| assertEquals(0, |
| RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_ETC1) |
| - oldEtc1Count); |
| assertEquals(0, |
| RecordHistogram.getHistogramValueCountForTesting( |
| TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT, |
| TabContentManager.ThumbnailFetchingResult.GOT_NOTHING) |
| - oldNothingCount); |
| onView(withId(R.id.tab_list_view)) |
| .check(ThumbnailAspectRatioAssertion.havingAspectRatio(2.0)); |
| } |
| |
| @Test |
| @MediumTest |
| // clang-format off |
| @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M, |
| message = "https://crbug.com/1023833") |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| public void testRecycling_defaultAspectRatio() throws InterruptedException { |
| // clang-format on |
| prepareTabs(10, 0, mUrl); |
| ChromeTabUtils.switchTabInCurrentTabModel(mActivityTestRule.getActivity(), 0); |
| enterGTSWithThumbnailChecking(); |
| onView(withId(R.id.tab_list_view)).perform(RecyclerViewActions.scrollToPosition(9)); |
| } |
| |
| @Test |
| @MediumTest |
| // clang-format off |
| @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M, |
| message = "https://crbug.com/1023833") |
| @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/0.75"}) |
| public void testRecycling_aspectRatioPoint75() throws InterruptedException { |
| // clang-format on |
| prepareTabs(10, 0, mUrl); |
| ChromeTabUtils.switchTabInCurrentTabModel(mActivityTestRule.getActivity(), 0); |
| enterGTSWithThumbnailChecking(); |
| onView(withId(R.id.tab_list_view)).perform(RecyclerViewActions.scrollToPosition(9)); |
| } |
| |
| @Test |
| @MediumTest |
| // clang-format off |
| @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M, |
| message = "https://crbug.com/1023833") |
| @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/0.75"}) |
| public void testExpandTab_withAspectRatioPoint75() throws InterruptedException { |
| // clang-format on |
| prepareTabs(1, 0, mUrl); |
| enterGTSWithThumbnailChecking(); |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature("NewTabVariation") |
| // clang-format off |
| @Features.DisableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study", |
| ChromeFeatureList.CLOSE_TAB_SUGGESTIONS}) |
| @CommandLineFlags.Add({BASE_PARAMS + "/tab_grid_layout_android_new_tab/NewTabVariation"}) |
| public void testNewTabVariation() throws InterruptedException { |
| // clang-format on |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| prepareTabs(2, 0, null); |
| enterTabSwitcher(cta); |
| verifyTabModelTabCount(cta, 2, 0); |
| checkNewTabVariationVisibility(true); |
| |
| createTabs(cta, true, 1); |
| verifyTabModelTabCount(cta, 2, 1); |
| enterTabSwitcher(cta); |
| checkNewTabVariationVisibility(false); |
| |
| switchTabModel(false); |
| checkNewTabVariationVisibility(false); |
| |
| switchTabModel(true); |
| checkNewTabVariationVisibility(false); |
| |
| closeFirstTabInTabSwitcher(); |
| verifyTabModelTabCount(cta, 2, 0); |
| checkNewTabVariationVisibility(true); |
| |
| createTabs(cta, true, 2); |
| verifyTabModelTabCount(cta, 2, 2); |
| enterTabSwitcher(cta); |
| checkNewTabVariationVisibility(false); |
| |
| MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(), cta, |
| R.id.close_all_incognito_tabs_menu_id); |
| verifyTabModelTabCount(cta, 2, 0); |
| checkNewTabVariationVisibility(true); |
| } |
| |
| private void checkNewTabVariationVisibility(boolean isVisible) { |
| if (isVisible) { |
| onView(allOf(withId(R.id.incognito_toggle_tabs), |
| withParent(withId(R.id.tab_switcher_toolbar)))) |
| .check(matches(withEffectiveVisibility(GONE))); |
| onView(allOf(withId(R.id.new_tab_button), |
| withParent(withId(R.id.tab_switcher_toolbar)))) |
| .check(matches(withEffectiveVisibility(GONE))); |
| onView(allOf(withId(R.id.new_tab_view), withParent(withId(R.id.tab_switcher_toolbar)))) |
| .check(matches(isDisplayed())); |
| } else { |
| onView(allOf(withId(R.id.incognito_toggle_tabs), |
| withParent(withId(R.id.tab_switcher_toolbar)))) |
| .check(matches(isDisplayed())); |
| onView(allOf(withId(R.id.new_tab_button), |
| withParent(withId(R.id.tab_switcher_toolbar)))) |
| .check(matches(isDisplayed())); |
| onView(allOf(withId(R.id.new_tab_view), withParent(withId(R.id.tab_switcher_toolbar)))) |
| .check(matches(withEffectiveVisibility(GONE))); |
| } |
| } |
| |
| @Test |
| @MediumTest |
| // clang-format off |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID, |
| ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID}) |
| public void testCloseTabViaCloseButton() throws Exception { |
| // clang-format on |
| mActivityTestRule.getActivity().getSnackbarManager().disableForTesting(); |
| prepareTabs(1, 0, null); |
| enterGTSWithThumbnailChecking(); |
| |
| onView(allOf(withId(R.id.action_button), withParent(withId(R.id.content_view)))) |
| .perform(click()); |
| } |
| |
| @Test |
| @MediumTest |
| @CommandLineFlags.Add({BASE_PARAMS}) |
| @EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID}) |
| public void testSwipeToDismiss_GTS() throws Exception { |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| // Create 3 tabs and merge the first two tabs into one group. |
| createTabs(cta, false, 3); |
| enterTabSwitcher(cta); |
| TabModel normalTabModel = cta.getTabModelSelector().getModel(false); |
| List<Tab> tabGroup = new ArrayList<>( |
| Arrays.asList(normalTabModel.getTabAt(0), normalTabModel.getTabAt(1))); |
| createTabGroup(cta, false, tabGroup); |
| verifyTabSwitcherCardCount(cta, 2); |
| verifyTabModelTabCount(cta, 3, 0); |
| |
| // Swipe to dismiss a single tab card. |
| onView(allOf(withParent(withId(R.id.compositor_view_holder)), withId(R.id.tab_list_view))) |
| .perform(RecyclerViewActions.actionOnItemAtPosition( |
| 1, getSwipeToDismissAction(false))); |
| verifyTabSwitcherCardCount(cta, 1); |
| verifyTabModelTabCount(cta, 2, 0); |
| |
| // Swipe to dismiss a tab group card. |
| onView(allOf(withParent(withId(R.id.compositor_view_holder)), withId(R.id.tab_list_view))) |
| .perform(RecyclerViewActions.actionOnItemAtPosition( |
| 0, getSwipeToDismissAction(true))); |
| verifyTabSwitcherCardCount(cta, 0); |
| verifyTabModelTabCount(cta, 0, 0); |
| } |
| |
| private static class TabCountAssertion implements ViewAssertion { |
| private int mExpectedCount; |
| |
| public static TabCountAssertion havingTabCount(int tabCount) { |
| return new TabCountAssertion(tabCount); |
| } |
| |
| public TabCountAssertion(int expectedCount) { |
| mExpectedCount = expectedCount; |
| } |
| |
| @Override |
| public void check(View view, NoMatchingViewException noMatchException) { |
| if (noMatchException != null) throw noMatchException; |
| |
| RecyclerView.Adapter adapter = ((RecyclerView) view).getAdapter(); |
| assertEquals(mExpectedCount, adapter.getItemCount()); |
| } |
| } |
| |
| private static class ThumbnailAspectRatioAssertion implements ViewAssertion { |
| private double mExpectedRatio; |
| |
| public static ThumbnailAspectRatioAssertion havingAspectRatio(double ratio) { |
| return new ThumbnailAspectRatioAssertion(ratio); |
| } |
| |
| private ThumbnailAspectRatioAssertion(double expectedRatio) { |
| mExpectedRatio = expectedRatio; |
| } |
| |
| @Override |
| public void check(View view, NoMatchingViewException noMatchException) { |
| if (noMatchException != null) throw noMatchException; |
| |
| RecyclerView recyclerView = (RecyclerView) view; |
| |
| RecyclerView.Adapter adapter = recyclerView.getAdapter(); |
| for (int i = 0; i < adapter.getItemCount(); i++) { |
| RecyclerView.ViewHolder viewHolder = |
| recyclerView.findViewHolderForAdapterPosition(i); |
| if (viewHolder != null) { |
| ViewLookupCachingFrameLayout tabView = |
| (ViewLookupCachingFrameLayout) viewHolder.itemView; |
| ImageView thumbnail = (ImageView) tabView.fastFindViewById(R.id.tab_thumbnail); |
| BitmapDrawable drawable = (BitmapDrawable) thumbnail.getDrawable(); |
| Bitmap bitmap = drawable.getBitmap(); |
| double bitmapRatio = bitmap.getWidth() * 1.0 / bitmap.getHeight(); |
| assertTrue( |
| "Actual ratio: " + bitmapRatio + "; Expected ratio: " + mExpectedRatio, |
| Math.abs(bitmapRatio - mExpectedRatio) |
| <= TabContentManager.ASPECT_RATIO_PRECISION); |
| } |
| } |
| } |
| } |
| |
| @Test |
| @MediumTest |
| // Disable TAB_TO_GTS_ANIMATION to make it less flaky. |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/enable_search_term_chip/true"}) |
| public void testSearchTermChip_noChip() throws InterruptedException { |
| assertTrue(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP.getValue()); |
| prepareTabs(1, 0, mUrl); |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(withId(R.id.search_button)).check(matches(not(isDisplayed()))); |
| } |
| |
| @Test |
| @MediumTest |
| // Disable TAB_TO_GTS_ANIMATION to make it less flaky. |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + "/enable_search_term_chip/true"}) |
| public void testSearchTermChip_withChip() throws InterruptedException { |
| assertTrue(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP.getValue()); |
| // Make sure we support RTL and CJKV languages. |
| String searchTermWithSpecialCodePoints = "a\n ئۇيغۇرچە\u200Eæ¼¢å—"; |
| // Special code points like new line (\n) and left-to-right marker (‎‎‎\u200E) should |
| // be stripped out. See TabAttributeCache#removeEscapedCodePoints for more details. |
| String expectedTerm = "a ئۇيغۇرچە漢å—"; |
| |
| String anotherTerm = "hello world"; |
| |
| // Do search, and verify the chip is still not shown. |
| AtomicReference<String> searchUrl = new AtomicReference<>(); |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| TemplateUrlServiceFactory.get().setSearchEngine("google.com"); |
| searchUrl.set(TemplateUrlServiceFactory.get().getUrlForSearchQuery( |
| searchTermWithSpecialCodePoints)); |
| cta.getTabModelSelector().getCurrentTab().loadUrl(new LoadUrlParams(searchUrl.get())); |
| }); |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(withId(R.id.search_button)).check(matches(not(isDisplayed()))); |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| |
| // Navigate, and verify the chip is shown. |
| mActivityTestRule.loadUrl(mUrl); |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(allOf(withParent(withId(R.id.search_button)), withText(expectedTerm))) |
| .check(matches(isDisplayed())); |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| |
| // Do another search, and verify the chip is gone. |
| AtomicReference<String> searchUrl2 = new AtomicReference<>(); |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| TemplateUrlServiceFactory.get().setSearchEngine("google.com"); |
| searchUrl2.set(TemplateUrlServiceFactory.get().getUrlForSearchQuery(anotherTerm)); |
| cta.getTabModelSelector().getCurrentTab().loadUrl(new LoadUrlParams(searchUrl2.get())); |
| }); |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(withId(R.id.search_button)).check(matches(not(isDisplayed()))); |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| |
| // Back to previous page, and verify the chip is back. |
| Espresso.pressBack(); |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(allOf(withParent(withId(R.id.search_button)), withText(expectedTerm))) |
| .check(matches(isDisplayed())); |
| |
| // Click the chip and check the tab navigates back to the search result page. |
| assertEquals(mUrl, cta.getTabModelSelector().getCurrentTab().getUrlString()); |
| OverviewModeBehaviorWatcher hideWatcher = TabUiTestHelper.createOverviewHideWatcher(cta); |
| onView(allOf(withParent(withId(R.id.search_button)), withText(expectedTerm))) |
| .perform(click()); |
| hideWatcher.waitForBehavior(); |
| CriteriaHelper.pollUiThread(Criteria.equals( |
| searchUrl.get(), () -> cta.getTabModelSelector().getCurrentTab().getUrlString())); |
| |
| // Verify the chip is gone. |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(withId(R.id.search_button)).check(matches(not(isDisplayed()))); |
| } |
| |
| @Test |
| @MediumTest |
| // clang-format off |
| // Disable TAB_TO_GTS_ANIMATION to make it less flaky. |
| @DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study") |
| @CommandLineFlags.Add({BASE_PARAMS + |
| "/enable_search_term_chip/true/enable_search_term_chip_adaptive_icon/true"}) |
| public void testSearchTermChip_adaptiveIcon() throws InterruptedException { |
| // clang-format on |
| assertTrue(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP.getValue()); |
| assertTrue(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP_ADAPTIVE.getValue()); |
| String searchTerm = "hello world"; |
| |
| // Do search, and verify the chip is still not shown. |
| AtomicReference<String> searchUrl = new AtomicReference<>(); |
| ChromeTabbedActivity cta = mActivityTestRule.getActivity(); |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| TemplateUrlServiceFactory.get().setSearchEngine("google.com"); |
| searchUrl.set(TemplateUrlServiceFactory.get().getUrlForSearchQuery(searchTerm)); |
| cta.getTabModelSelector().getCurrentTab().loadUrl(new LoadUrlParams(searchUrl.get())); |
| }); |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(withId(R.id.search_button)).check(matches(not(isDisplayed()))); |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| |
| // Navigate, and verify the chip is shown. |
| mActivityTestRule.loadUrl(mUrl); |
| enterGTSWithThumbnailChecking(); |
| |
| onView(withId(R.id.tab_list_view)).check(TabCountAssertion.havingTabCount(1)); |
| onView(allOf(withParent(withId(R.id.search_button)), withText(searchTerm))) |
| .check(matches(isDisplayed())); |
| |
| // Switch the default search engine from google.com to yahoo.com, the search chip icon |
| // should change. |
| RecyclerView tabListRecyclerView = cta.findViewById(R.id.tab_list_view); |
| ChipView chipView = |
| tabListRecyclerView.findViewHolderForAdapterPosition(0).itemView.findViewById( |
| R.id.search_button); |
| ChromeImageView iconImageView = (ChromeImageView) chipView.getChildAt(0); |
| Drawable googleDrawable = iconImageView.getDrawable(); |
| |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> { TemplateUrlServiceFactory.get().setSearchEngine("yahoo.com"); }); |
| |
| assertNotEquals(googleDrawable, iconImageView.getDrawable()); |
| } |
| |
| private void switchTabModel(boolean isIncognito) { |
| assertTrue(isIncognito != |
| mActivityTestRule.getActivity().getTabModelSelector().isIncognitoSelected()); |
| |
| onView(withContentDescription( |
| isIncognito ? R.string.accessibility_tab_switcher_incognito_stack |
| : R.string.accessibility_tab_switcher_standard_stack) |
| ).perform(click()); |
| |
| CriteriaHelper.pollUiThread(Criteria.equals(isIncognito, |
| () -> mActivityTestRule.getActivity().getTabModelSelector().isIncognitoSelected())); |
| } |
| |
| /** |
| * TODO(wychen): move some of the callers to {@link TabUiTestHelper#enterTabSwitcher}. |
| */ |
| private void enterGTSWithThumbnailChecking() throws InterruptedException { |
| Tab currentTab = mActivityTestRule.getActivity().getTabModelSelector().getCurrentTab(); |
| // Native tabs need to be invalidated first to trigger thumbnail taking, so skip them. |
| boolean checkThumbnail = !currentTab.isNativePage(); |
| |
| if (checkThumbnail) { |
| mActivityTestRule.getActivity().getTabContentManager().removeTabThumbnail( |
| currentTab.getId()); |
| } |
| |
| int count = getCaptureCount(); |
| waitForCaptureRateControl(); |
| // TODO(wychen): use TabUiTestHelper.enterTabSwitcher() instead. |
| // Might increase flakiness though. See crbug.com/1024742. |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> mActivityTestRule.getActivity().getLayoutManager().showOverview(true)); |
| assertTrue(mActivityTestRule.getActivity().getLayoutManager().overviewVisible()); |
| |
| // Make sure the fading animation is done. |
| int delta; |
| if (TextUtils.equals( |
| mActivityTestRule.getActivity().getCurrentWebContents().getLastCommittedUrl(), |
| NTP_URL)) { |
| // NTP is not invalidated, so no new captures. |
| delta = 0; |
| } else { |
| // The final capture at StartSurfaceLayout#finishedShowing time. |
| delta = 1; |
| if (ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_TO_GTS_ANIMATION) |
| && areAnimatorsEnabled()) { |
| // The faster capturing without writing back to cache. |
| delta += 1; |
| } |
| } |
| checkCaptureCount(delta, count); |
| TabUiTestHelper.verifyAllTabsHaveThumbnail( |
| mActivityTestRule.getActivity().getCurrentTabModel()); |
| } |
| |
| /** |
| * TODO(wychen): create a version without thumbnail checking, which uses |
| * {@link TabUiTestHelper#clickFirstCardFromTabSwitcher} or simply {@link Espresso#pressBack}, |
| * and {@link OverviewModeBehaviorWatcher}. |
| */ |
| private void leaveGTSAndVerifyThumbnailsAreReleased() { |
| assertTrue(mActivityTestRule.getActivity().getLayoutManager().overviewVisible()); |
| |
| StartSurface startSurface = mStartSurfaceLayout.getStartSurfaceForTesting(); |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> { startSurface.getController().onBackPressed(); }); |
| // TODO(wychen): using default timeout or even converting to |
| // OverviewModeBehaviorWatcher shouldn't increase flakiness. |
| CriteriaHelper.pollUiThread( |
| () |
| -> !mActivityTestRule.getActivity().getLayoutManager().overviewVisible(), |
| "Overview not hidden yet", DEFAULT_MAX_TIME_TO_POLL * 10, DEFAULT_POLLING_INTERVAL); |
| assertThumbnailsAreReleased(); |
| } |
| |
| private void checkFinalCaptureCount(boolean switchToAnotherTab, int initCount) { |
| int expected; |
| if (TextUtils.equals( |
| mActivityTestRule.getActivity().getCurrentWebContents().getLastCommittedUrl(), |
| NTP_URL)) { |
| expected = 0; |
| } else { |
| expected = mRepeat; |
| if (ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_TO_GTS_ANIMATION) |
| && areAnimatorsEnabled()) { |
| expected += mRepeat; |
| } |
| if (switchToAnotherTab) { |
| expected += mRepeat; |
| } |
| } |
| checkCaptureCount(expected, initCount); |
| } |
| |
| private void checkCaptureCount(int expectedDelta, int initCount) { |
| // TODO(wychen): With animation, the 2nd capture might be skipped if the 1st takes too long. |
| CriteriaHelper.pollUiThread( |
| Criteria.equals(expectedDelta, () -> getCaptureCount() - initCount)); |
| } |
| |
| private int getCaptureCount() { |
| return RecordHistogram.getHistogramTotalCountForTesting("Compositing.CopyFromSurfaceTime"); |
| } |
| |
| private void waitForCaptureRateControl() throws InterruptedException { |
| // Needs to wait for at least |kCaptureMinRequestTimeMs| in order to capture another one. |
| // TODO(wychen): find out why waiting is still needed after setting |
| // |kCaptureMinRequestTimeMs| to 0. |
| Thread.sleep(2000); |
| } |
| |
| private void assertThumbnailsAreReleased() { |
| // Could not directly assert canAllBeGarbageCollected() because objects can be in Cleaner. |
| CriteriaHelper.pollUiThread(() -> canAllBeGarbageCollected(mAllBitmaps)); |
| } |
| |
| private boolean canAllBeGarbageCollected(List<WeakReference<Bitmap>> bitmaps) { |
| for (WeakReference<Bitmap> bitmap : bitmaps) { |
| if (!GarbageCollectionTestUtils.canBeGarbageCollected(bitmap)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void simulateAspectRatioChangedToPoint75() throws IOException { |
| TabModel currentModel = mActivityTestRule.getActivity().getCurrentTabModel(); |
| for (int i = 0; i < currentModel.getCount(); i++) { |
| Tab tab = currentModel.getTabAt(i); |
| Bitmap bitmap = TabContentManager.getJpegForTab(tab.getId()); |
| bitmap = Bitmap.createScaledBitmap( |
| bitmap, bitmap.getWidth(), (int) (bitmap.getWidth() * 1.0 / 0.75), false); |
| encodeJpeg(tab, bitmap); |
| } |
| } |
| |
| private void encodeJpeg(Tab tab, Bitmap bitmap) throws IOException { |
| FileOutputStream outputStream = |
| new FileOutputStream(TabContentManager.getTabThumbnailFileJpeg(tab.getId())); |
| bitmap.compress(Bitmap.CompressFormat.JPEG, 50, outputStream); |
| outputStream.close(); |
| Bitmap decodedBitmap = TabContentManager.getJpegForTab(tab.getId()); |
| } |
| |
| private void verifyAllThumbnailHasAspectRatio(double ratio) { |
| TabModel currentModel = mActivityTestRule.getActivity().getCurrentTabModel(); |
| for (int i = 0; i < currentModel.getCount(); i++) { |
| Tab tab = currentModel.getTabAt(i); |
| Bitmap bitmap = TabContentManager.getJpegForTab(tab.getId()); |
| double bitmapRatio = bitmap.getWidth() * 1.0 / bitmap.getHeight(); |
| assertTrue("Actual ratio: " + bitmapRatio + "; Expected ratio: " + ratio, |
| Math.abs(bitmapRatio - ratio) <= TabContentManager.ASPECT_RATIO_PRECISION); |
| } |
| } |
| |
| private void verifyOnlyOneTabSuggestionMessageCardIsShowing() throws InterruptedException { |
| String suggestionMessageTemplate = mActivityTestRule.getActivity().getString( |
| org.chromium.chrome.tab_ui.R.string.tab_suggestion_close_stale_message); |
| String suggestionMessage = |
| String.format(Locale.getDefault(), suggestionMessageTemplate, "3"); |
| prepareTabs(3, 0, mUrl); |
| CriteriaHelper.pollUiThread(TabSuggestionMessageService::isSuggestionAvailableForTesting); |
| CriteriaHelper.pollUiThread(Criteria.equals(3, this::getTabCountInCurrentTabModel)); |
| |
| enterGTSWithThumbnailChecking(); |
| CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting); |
| onView(allOf(withText(suggestionMessage), withParent(withId(R.id.tab_grid_message_item)))) |
| .check(matches(isDisplayed())); |
| leaveGTSAndVerifyThumbnailsAreReleased(); |
| |
| // With soft or hard clean up depends on the soft-cleanup-delay and cleanup-delay params. |
| enterGTSWithThumbnailChecking(); |
| CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting); |
| // This will fail with error "matched multiple views" when there is more than one suggestion |
| // message card. |
| onView(allOf(withText(suggestionMessage), withParent(withId(R.id.tab_grid_message_item)))) |
| .check(matches(isDisplayed())); |
| } |
| } |