blob: 3fcd18c64da832a294ca915de32433cadd9d2619 [file] [log] [blame]
// 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.browser.tasks.tab_management;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.chromium.base.test.util.CallbackHelper.WAIT_TIMEOUT_SECONDS;
import static org.chromium.chrome.browser.tabmodel.TabSelectionType.FROM_USER;
import static org.chromium.chrome.browser.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.support.annotation.Nullable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.chromium.base.Log;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.compositor.animation.CompositorAnimator;
import org.chromium.chrome.browser.compositor.layouts.Layout;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.util.FeatureUtilities;
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.ChromeTabUtils;
import org.chromium.chrome.test.util.MenuUtils;
import org.chromium.chrome.test.util.browser.Features;
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.test.util.UiRestriction;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/** Tests for the {@link GridTabSwitcherLayout}, mainly for animation performance. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
"enable-features=" + ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study",
"force-fieldtrials=Study/Group"})
@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
public class GridTabSwitcherLayoutPerfTest {
private static final String TAG = "GTSLayoutTest";
/** Flip this to {@code true} to run performance tests locally. */
private static final boolean PERF_RUN = false;
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Rule
public TestRule mProcessor = new Features.InstrumentationProcessor();
private EmbeddedTestServer mTestServer;
private GridTabSwitcherLayout mGtsLayout;
private String mUrl;
private int mRepeat;
private long mWaitingTime;
private int mTabNumCap;
@Before
public void setUp() throws InterruptedException {
FeatureUtilities.setGridTabSwitcherEnabledForTesting(true);
mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
mActivityTestRule.startMainActivityFromLauncher();
Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
assertTrue(layout instanceof GridTabSwitcherLayout);
mGtsLayout = (GridTabSwitcherLayout) layout;
mUrl = mTestServer.getURL("/chrome/test/data/android/navigate/simple.html");
mRepeat = 3;
mWaitingTime = 0;
mTabNumCap = 3;
if (PERF_RUN) {
mRepeat = 30;
// Wait before the animation to get more stable results.
mWaitingTime = 1000;
mTabNumCap = 0;
}
}
@Test
@MediumTest
@CommandLineFlags.
Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
public void testTabToGridFromLiveTab() throws InterruptedException {
prepareTabs(1, NTP_URL);
reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab");
}
@Test
@MediumTest
@CommandLineFlags.
Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
public void testTabToGridFromLiveTabWith10Tabs() throws InterruptedException {
prepareTabs(10, NTP_URL);
reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs");
}
@Test
@MediumTest
@CommandLineFlags.
Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/10000/cleanup-delay/10000"})
public void testTabToGridFromLiveTabWith10TabsWarm() throws InterruptedException {
prepareTabs(10, NTP_URL);
reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs (warm)");
}
@Test
@MediumTest
@CommandLineFlags.
Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/10000"})
public void testTabToGridFromLiveTabWith10TabsSoft() throws InterruptedException {
prepareTabs(10, NTP_URL);
reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs (soft)");
}
@Test
@MediumTest
@CommandLineFlags.
Add({"force-fieldtrial-params=Study.Group:downsampling-scale/1/soft-cleanup-delay/0/cleanup-delay/0"})
public void testTabToGridFromLiveTabWith10TabsNoDownsample() throws InterruptedException {
prepareTabs(10, NTP_URL);
reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs (no downsample)");
}
@Test
@MediumTest
@CommandLineFlags.
Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
public void testTabToGridFromLiveTabWith10TabsWithoutThumbnail() throws InterruptedException {
// Note that most of the tabs won't have thumbnails.
prepareTabs(10, null);
reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs without thumbnails");
}
@Test
@LargeTest
@CommandLineFlags.
Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
public void testTabToGridFromLiveTabWith100Tabs() throws InterruptedException {
// Skip waiting for loading. Otherwise it would take too long.
// Note that most of the tabs won't have thumbnails.
prepareTabs(100, null);
reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 100 tabs without thumbnails");
}
@Test
@MediumTest
@CommandLineFlags.
Add({"force-fieldtrial-params=Study.Group:soft-cleanup-delay/0/cleanup-delay/0"})
public void testTabToGridFromNtp() throws InterruptedException {
prepareTabs(1, NTP_URL);
reportTabToGridPerf(NTP_URL, "Tab-to-Grid from NTP");
}
/**
* Make Chrome have {@code numTabs} or Tabs with {@code url} loaded.
* @param url The URL to load. Skip loading when null, but the thumbnail for the NTP might not
* be saved.
*/
private void prepareTabs(int numTabs, @Nullable String url) throws InterruptedException {
assertTrue(numTabs >= 1);
assertEquals(1, mActivityTestRule.getActivity().getTabModelSelector().getTotalTabCount());
// Only run the full size when doing local perf tests.
if (mTabNumCap > 0) numTabs = Math.min(numTabs, mTabNumCap);
if (url != null) mActivityTestRule.loadUrl(url);
for (int i = 0; i < numTabs - 1; i++) {
MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
mActivityTestRule.getActivity(), R.id.new_tab_menu_id);
if (url != null) mActivityTestRule.loadUrl(url);
}
ChromeTabUtils.waitForTabPageLoaded(mActivityTestRule.getActivity().getActivityTab(), null,
null, WAIT_TIMEOUT_SECONDS * 10);
assertEquals(
numTabs, mActivityTestRule.getActivity().getTabModelSelector().getTotalTabCount());
}
private void reportTabToGridPerf(String fromUrl, String description)
throws InterruptedException {
List<Float> frameRates = new LinkedList<>();
List<Float> frameInterval = new LinkedList<>();
List<Float> dirtySpans = new LinkedList<>();
GridTabSwitcherLayout.PerfListener collector =
(frameRendered, elapsedMs, maxFrameInterval, dirtySpan) -> {
assertTrue(elapsedMs
>= GridTabSwitcherLayout.ZOOMING_DURATION * CompositorAnimator.sDurationScale);
float fps = 1000.f * frameRendered / elapsedMs;
frameRates.add(fps);
frameInterval.add((float) maxFrameInterval);
dirtySpans.add((float) dirtySpan);
};
mActivityTestRule.loadUrl(fromUrl);
Thread.sleep(mWaitingTime);
GridTabSwitcher gts = mGtsLayout.getGridTabSwitcherForTesting();
for (int i = 0; i < mRepeat; i++) {
mGtsLayout.setPerfListenerForTesting(collector);
Thread.sleep(mWaitingTime);
TestThreadUtils.runOnUiThreadBlocking(
() -> mActivityTestRule.getActivity().getLayoutManager().showOverview(true));
final int expectedSize = i + 1;
CriteriaHelper.pollInstrumentationThread(() -> frameRates.size() == expectedSize,
"Have not got PerfListener callback", DEFAULT_MAX_TIME_TO_POLL * 10,
DEFAULT_POLLING_INTERVAL);
assertTrue(mActivityTestRule.getActivity().getLayoutManager().overviewVisible());
mGtsLayout.setPerfListenerForTesting(null);
// Make sure the fading animation is done.
Thread.sleep(1000);
TestThreadUtils.runOnUiThreadBlocking(
() -> gts.getGridController().hideOverview(false));
Thread.sleep(1000);
CriteriaHelper.pollInstrumentationThread(
() -> !mActivityTestRule.getActivity().getLayoutManager().overviewVisible(),
"Overview not hidden yet", DEFAULT_MAX_TIME_TO_POLL * 10,
DEFAULT_POLLING_INTERVAL);
}
assertEquals(mRepeat, frameRates.size());
Log.i(TAG, "%s: fps = %.2f, maxFrameInterval = %.0f, dirtySpan = %.0f", description,
median(frameRates), median(frameInterval), median(dirtySpans));
}
@Test
@MediumTest
public void testGridToTabToCurrentNTP() throws InterruptedException {
prepareTabs(1, NTP_URL);
reportGridToTabPerf(false, false, "Grid-to-Tab to current NTP");
}
@Test
@MediumTest
public void testGridToTabToOtherNTP() throws InterruptedException {
prepareTabs(2, NTP_URL);
reportGridToTabPerf(true, false, "Grid-to-Tab to other NTP");
}
@Test
@MediumTest
public void testGridToTabToCurrentLive() throws InterruptedException {
prepareTabs(1, mUrl);
reportGridToTabPerf(false, false, "Grid-to-Tab to current live tab");
}
@Test
@MediumTest
public void testGridToTabToOtherLive() throws InterruptedException {
prepareTabs(2, mUrl);
reportGridToTabPerf(true, false, "Grid-to-Tab to other live tab");
}
@Test
@MediumTest
public void testGridToTabToOtherFrozen() throws InterruptedException {
prepareTabs(2, mUrl);
reportGridToTabPerf(true, true, "Grid-to-Tab to other frozen tab");
}
private void reportGridToTabPerf(boolean switchToAnotherTab, boolean killBeforeSwitching,
String description) throws InterruptedException {
List<Float> frameRates = new LinkedList<>();
List<Float> frameInterval = new LinkedList<>();
GridTabSwitcherLayout.PerfListener collector =
(frameRendered, elapsedMs, maxFrameInterval, dirtySpan) -> {
assertTrue(elapsedMs
>= GridTabSwitcherLayout.ZOOMING_DURATION * CompositorAnimator.sDurationScale);
float fps = 1000.f * frameRendered / elapsedMs;
frameRates.add(fps);
frameInterval.add((float) maxFrameInterval);
};
Thread.sleep(mWaitingTime);
for (int i = 0; i < mRepeat; i++) {
mGtsLayout.setPerfListenerForTesting(null);
TestThreadUtils.runOnUiThreadBlocking(
() -> mActivityTestRule.getActivity().getLayoutManager().showOverview(false));
assertTrue(mActivityTestRule.getActivity().getLayoutManager().overviewVisible());
Thread.sleep(1000);
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);
Thread.sleep(1000);
}
mGtsLayout.setPerfListenerForTesting(collector);
Thread.sleep(mWaitingTime);
TestThreadUtils.runOnUiThreadBlocking(
()
-> mActivityTestRule.getActivity().getCurrentTabModel().setIndex(
targetIndex, FROM_USER));
final int expectedSize = i + 1;
CriteriaHelper.pollInstrumentationThread(() -> frameRates.size() == expectedSize,
"Have not got PerfListener callback", DEFAULT_MAX_TIME_TO_POLL * 10,
DEFAULT_POLLING_INTERVAL);
CriteriaHelper.pollInstrumentationThread(
() -> !mActivityTestRule.getActivity().getLayoutManager().overviewVisible(),
"Overview not hidden yet");
Thread.sleep(1000);
}
assertEquals(mRepeat, frameRates.size());
Log.i(TAG, "%s: fps = %.2f, maxFrameInterval = %.0f", description, median(frameRates),
median(frameInterval));
}
private float median(List<Float> list) {
float[] array = new float[list.size()];
for (int i = 0; i < array.length; i++) {
array[i] = list.get(i);
}
Arrays.sort(array);
return array[array.length / 2];
}
}