Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4367)

Unified Diff: chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ceb1636102df1941c251e2d536f109b3a8c305c
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
@@ -0,0 +1,447 @@
+// Copyright 2015 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;
+
+import android.test.FlakyTest;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.SparseArray;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.compositor.layouts.Layout;
+import org.chromium.chrome.browser.tabmodel.ChromeTabCreator;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.chrome.test.ChromeActivityTestCaseBase;
+import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.TestHttpServerClient;
+import org.chromium.content.browser.BindingManager;
+import org.chromium.content.browser.ChildProcessConnection;
+import org.chromium.content.browser.ChildProcessLauncher;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.base.DeviceFormFactor;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Integration tests for the BindingManager API. This test plants a mock BindingManager
+ * implementation and verifies that the signals it relies on are correctly delivered.
+ */
+@CommandLineFlags.Add(ChromeSwitches.DISABLE_DOCUMENT_MODE) // crbug.com/414719
+public class BindingManagerIntegrationTest extends ChromeActivityTestCaseBase<ChromeActivity> {
+
+ private static class MockBindingManager implements BindingManager {
+ // Maps pid to the last received visibility state of the renderer.
+ private final SparseArray<Boolean> mProcessInForegroundMap = new SparseArray<Boolean>();
+ // Maps pid to a string recording calls to setInForeground() and visibilityDetermined().
+ private final SparseArray<String> mVisibilityCallsMap = new SparseArray<String>();
+
+ boolean isInForeground(int pid) {
+ return mProcessInForegroundMap.get(pid);
+ }
+
+ boolean isInBackground(int pid) {
+ return !mProcessInForegroundMap.get(pid);
+ }
+
+ boolean setInForegroundWasCalled(int pid) {
+ return mProcessInForegroundMap.get(pid) != null;
+ }
+
+ String getVisibilityCalls(int pid) {
+ return mVisibilityCallsMap.get(pid);
+ }
+
+ @Override
+ public void addNewConnection(int pid, ChildProcessConnection connection) {
+ mVisibilityCallsMap.put(pid, "");
+ }
+
+ @Override
+ public void setInForeground(int pid, boolean inForeground) {
+ mProcessInForegroundMap.put(pid, inForeground);
+
+ if (inForeground) {
+ mVisibilityCallsMap.put(pid, mVisibilityCallsMap.get(pid) + "FG;");
+ } else {
+ mVisibilityCallsMap.put(pid, mVisibilityCallsMap.get(pid) + "BG;");
+ }
+ }
+
+ @Override
+ public void determinedVisibility(int pid) {
+ mVisibilityCallsMap.put(pid, mVisibilityCallsMap.get(pid) + "DETERMINED;");
+ }
+
+ @Override
+ public void onSentToBackground() {}
+
+ @Override
+ public void onBroughtToForeground() {}
+
+ @Override
+ public boolean isOomProtected(int pid) {
+ return false;
+ }
+
+ @Override
+ public void clearConnection(int pid) {}
+ }
+
+ private MockBindingManager mBindingManager;
+
+ private static final String FILE_PATH = "chrome/test/data/android/test.html";
+ // about:version will always be handled by a different renderer than a local file.
+ private static final String ABOUT_VERSION_PATH = "chrome://version/";
+
+ public BindingManagerIntegrationTest() {
+ super(ChromeActivity.class);
+ }
+
+ /**
+ * Verifies that the .setProcessInForeground() signal is called correctly as the tabs are
+ * created and switched.
+ */
+ @LargeTest
+ @Feature({"ProcessManagement"})
+ public void testTabSwitching() throws InterruptedException {
+ // Create two tabs and wait until they are loaded, so that their renderers are around.
+ final Tab[] tabs = new Tab[2];
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ // Foreground tab.
+ ChromeTabCreator tabCreator = getActivity().getCurrentTabCreator();
+ tabs[0] = tabCreator.createNewTab(
+ new LoadUrlParams(TestHttpServerClient.getUrl(FILE_PATH)),
+ TabLaunchType.FROM_KEYBOARD, null);
+ // Background tab.
+ tabs[1] = tabCreator.createNewTab(
+ new LoadUrlParams(TestHttpServerClient.getUrl(FILE_PATH)),
+ TabLaunchType.FROM_LONGPRESS_BACKGROUND, null);
+ // On Svelte devices the background tab would not be loaded automatically, so
+ // trigger the load manually.
+ tabs[1].show(TabSelectionType.FROM_USER);
+ tabs[1].hide();
+ }
+ });
+ ChromeTabUtils.waitForTabPageLoaded(tabs[0], TestHttpServerClient.getUrl(FILE_PATH));
+ ChromeTabUtils.waitForTabPageLoaded(tabs[1], TestHttpServerClient.getUrl(FILE_PATH));
+
+ // Wait for the new tab animations on phones to finish.
+ if (!DeviceFormFactor.isTablet(getActivity())
+ && getActivity() instanceof CompositorChromeActivity) {
+ final CompositorChromeActivity activity = (CompositorChromeActivity) getActivity();
+ assertTrue("Did not finish animation",
+ CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ Layout layout = activity.getCompositorViewHolder()
+ .getLayoutManager().getActiveLayout();
+ return !layout.isLayoutAnimating();
+ }
+ }));
+ }
+ getInstrumentation().waitForIdleSync();
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure that the renderers were spawned.
+ assertTrue(tabs[0].getContentViewCore().getCurrentRenderProcessId() > 0);
+ assertTrue(tabs[1].getContentViewCore().getCurrentRenderProcessId() > 0);
+
+ // Verify that the renderer of the foreground tab was signalled as visible.
+ assertTrue(mBindingManager.isInForeground(
+ tabs[0].getContentViewCore().getCurrentRenderProcessId()));
+ // Verify that the renderer of the tab loaded in background was signalled as not
+ // visible.
+ assertTrue(mBindingManager.isInBackground(
+ tabs[1].getContentViewCore().getCurrentRenderProcessId()));
+
+ // Select tabs[1] and verify that the renderer visibility was flipped.
+ TabModelUtils.setIndex(getActivity().getCurrentTabModel(), indexOf(tabs[1]));
+ assertTrue(mBindingManager.isInBackground(
+ tabs[0].getContentViewCore().getCurrentRenderProcessId()));
+ assertTrue(mBindingManager.isInForeground(
+ tabs[1].getContentViewCore().getCurrentRenderProcessId()));
+ }
+ });
+ }
+
+ /**
+ * Verifies that the .setProcessInForeground() signal is called correctly when a tab that
+ * crashed in background is restored in foreground. This is a regression test for
+ * http://crbug.com/399521.
+ */
+ @LargeTest
+ @Feature({"ProcessManagement"})
+ public void testCrashInBackground() throws InterruptedException {
+ // Create two tabs and wait until they are loaded, so that their renderers are around.
+ final Tab[] tabs = new Tab[2];
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ // Foreground tab.
+ ChromeTabCreator tabCreator = getActivity().getCurrentTabCreator();
+ tabs[0] = tabCreator.createNewTab(
+ new LoadUrlParams(TestHttpServerClient.getUrl(FILE_PATH)),
+ TabLaunchType.FROM_KEYBOARD, null);
+ // Background tab.
+ tabs[1] = tabCreator.createNewTab(
+ new LoadUrlParams(TestHttpServerClient.getUrl(FILE_PATH)),
+ TabLaunchType.FROM_LONGPRESS_BACKGROUND, null);
+ // On Svelte devices the background tab would not be loaded automatically, so
+ // trigger the load manually.
+ tabs[1].show(TabSelectionType.FROM_USER);
+ tabs[1].hide();
+ }
+ });
+ ChromeTabUtils.waitForTabPageLoaded(tabs[0], TestHttpServerClient.getUrl(FILE_PATH));
+ ChromeTabUtils.waitForTabPageLoaded(tabs[1], TestHttpServerClient.getUrl(FILE_PATH));
+
+ // Wait for the new tab animations on phones to finish.
+ if (!DeviceFormFactor.isTablet(getActivity())
+ && getActivity() instanceof CompositorChromeActivity) {
+ final CompositorChromeActivity activity = (CompositorChromeActivity) getActivity();
+ assertTrue("Did not finish animation",
+ CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ Layout layout = activity.getCompositorViewHolder()
+ .getLayoutManager().getActiveLayout();
+ return !layout.isLayoutAnimating();
+ }
+ }));
+ }
+ getInstrumentation().waitForIdleSync();
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure that the renderers were spawned.
+ assertTrue(tabs[0].getContentViewCore().getCurrentRenderProcessId() > 0);
+ assertTrue(tabs[1].getContentViewCore().getCurrentRenderProcessId() > 0);
+
+ // Verify that the renderer of the foreground tab was signalled as visible.
+ assertTrue(mBindingManager.isInForeground(
+ tabs[0].getContentViewCore().getCurrentRenderProcessId()));
+ // Verify that the renderer of the tab loaded in background was signalled as not
+ // visible.
+ assertTrue(mBindingManager.isInBackground(
+ tabs[1].getContentViewCore().getCurrentRenderProcessId()));
+ }
+ });
+
+ // Kill the renderer and wait for the crash to be noted by the browser process.
+ assertTrue(ChildProcessLauncher.crashProcessForTesting(
+ tabs[1].getContentViewCore().getCurrentRenderProcessId()));
+
+ assertTrue("Renderer crash wasn't noticed by the browser.",
+ CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return tabs[1].getContentViewCore().getCurrentRenderProcessId() == 0;
+ }
+ }));
+
+ // Switch to the tab that crashed in background.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ TabModelUtils.setIndex(getActivity().getCurrentTabModel(), indexOf(tabs[1]));
+ }
+ });
+
+ // Wait until the process is spawned and its visibility is determined.
+ assertTrue("Process for the crashed tab was not respawned.",
+ CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return tabs[1].getContentViewCore().getCurrentRenderProcessId() != 0;
+ }
+ }));
+
+ assertTrue("isInForeground() was not called for the process.",
+ CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mBindingManager.setInForegroundWasCalled(
+ tabs[1].getContentViewCore().getCurrentRenderProcessId());
+ }
+ }));
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ // Verify the visibility of the renderers.
+ assertTrue(mBindingManager.isInBackground(
+ tabs[0].getContentViewCore().getCurrentRenderProcessId()));
+ assertTrue(mBindingManager.isInForeground(
+ tabs[1].getContentViewCore().getCurrentRenderProcessId()));
+ }
+ });
+ }
+
+ /**
+ * Verifies that a renderer that crashes in foreground has the correct visibility when
+ * recreated.
+ */
+ @LargeTest
+ @Feature({"ProcessManagement"})
+ public void testCrashInForeground() throws InterruptedException {
+ // Create a tab in foreground and wait until it is loaded.
+ final Tab tab = ThreadUtils.runOnUiThreadBlockingNoException(
+ new Callable<Tab>() {
+ @Override
+ public Tab call() throws Exception {
+ ChromeTabCreator tabCreator = getActivity().getCurrentTabCreator();
+ return tabCreator.createNewTab(
+ new LoadUrlParams(TestHttpServerClient.getUrl(FILE_PATH)),
+ TabLaunchType.FROM_KEYBOARD, null);
+ }
+ });
+ ChromeTabUtils.waitForTabPageLoaded(tab, TestHttpServerClient.getUrl(FILE_PATH));
+ getInstrumentation().waitForIdleSync();
+
+ // Kill the renderer and wait for the crash to be noted by the browser process.
+ assertTrue(ChildProcessLauncher.crashProcessForTesting(
+ tab.getContentViewCore().getCurrentRenderProcessId()));
+
+ assertTrue("Renderer crash wasn't noticed by the browser.",
+ CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return tab.getContentViewCore().getCurrentRenderProcessId() == 0;
+ }
+ }));
+
+ // Reload the tab, respawning the renderer.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ tab.reload();
+ }
+ });
+
+ // Wait until the process is spawned and its visibility is determined.
+ assertTrue("Process for the crashed tab was not respawned.",
+ CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return tab.getContentViewCore().getCurrentRenderProcessId() != 0;
+ }
+ }));
+
+ assertTrue("isInForeground() was not called for the process.",
+ CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mBindingManager.setInForegroundWasCalled(
+ tab.getContentViewCore().getCurrentRenderProcessId());
+ }
+ }));
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ // Verify the visibility of the renderer.
+ assertTrue(mBindingManager.isInForeground(
+ tab.getContentViewCore().getCurrentRenderProcessId()));
+ }
+ });
+ }
+
+ /**
+ * Ensures correctness of the visibilityDetermined() calls, that should be always preceded by
+ * setInForeground().
+ *
+ * Bug: https://crbug.com/474543
+ * @LargeTest
+ * @Feature({"ProcessManagement"})
+ */
+ @FlakyTest
+ public void testVisibilityDetermined() throws InterruptedException {
+ // Create a tab in foreground and wait until it is loaded.
+ final Tab fgTab = ThreadUtils.runOnUiThreadBlockingNoException(
+ new Callable<Tab>() {
+ @Override
+ public Tab call() {
+ ChromeTabCreator tabCreator = getActivity().getCurrentTabCreator();
+ return tabCreator.createNewTab(
+ new LoadUrlParams(TestHttpServerClient.getUrl(FILE_PATH)),
+ TabLaunchType.FROM_KEYBOARD, null);
+ }});
+ ChromeTabUtils.waitForTabPageLoaded(fgTab, TestHttpServerClient.getUrl(FILE_PATH));
+ int initialNavigationPid = fgTab.getContentViewCore().getCurrentRenderProcessId();
+ // Ensure the following calls happened:
+ // - FG - setInForeground(true) - when the tab is created in the foreground
+ // - DETERMINED - visibilityDetermined() - after the initial navigation is committed
+ assertEquals("FG;DETERMINED;", mBindingManager.getVisibilityCalls(initialNavigationPid));
+
+ // Navigate to about:version which requires a different renderer.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fgTab.loadUrl(new LoadUrlParams(ABOUT_VERSION_PATH));
+ }
+ });
+ ChromeTabUtils.waitForTabPageLoaded(fgTab, ABOUT_VERSION_PATH);
+ int secondNavigationPid = fgTab.getContentViewCore().getCurrentRenderProcessId();
+ assertTrue(secondNavigationPid != initialNavigationPid);
+ // Ensure the following calls happened:
+ // - BG - setInForeground(false) - when the renderer is created for uncommited frame
+ // - FG - setInForeground(true) - when the frame is swapped in on commit
+ // - DETERMINED - visibilityDetermined() - after the navigation is committed
+ assertEquals("BG;FG;DETERMINED;", mBindingManager.getVisibilityCalls(secondNavigationPid));
+
+ // Open a tab in the background and load it.
+ final Tab bgTab = ThreadUtils.runOnUiThreadBlockingNoException(
+ new Callable<Tab>() {
+ @Override
+ public Tab call() {
+ ChromeTabCreator tabCreator = getActivity().getCurrentTabCreator();
+ Tab tab = tabCreator.createNewTab(
+ new LoadUrlParams(TestHttpServerClient.getUrl(FILE_PATH)),
+ TabLaunchType.FROM_LONGPRESS_BACKGROUND, null);
+ // On Svelte devices the background tab would not be loaded automatically,
+ // so trigger the load manually.
+ tab.show(TabSelectionType.FROM_USER);
+ tab.hide();
+ return tab;
+ }});
+ ChromeTabUtils.waitForTabPageLoaded(bgTab, TestHttpServerClient.getUrl(FILE_PATH));
+ int bgNavigationPid = bgTab.getContentViewCore().getCurrentRenderProcessId();
+ // Ensure the following calls happened:
+ // - BG - setInForeground(false) - when tab is created in the background
+ // - DETERMINED - visibilityDetermined() - after the navigation is committed
+ assertEquals("BG;DETERMINED;", mBindingManager.getVisibilityCalls(bgNavigationPid));
+ }
+
+ @Override
+ public void startMainActivity() throws InterruptedException {
+ startMainActivityOnBlankPage();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // Hook in the test binding manager.
+ mBindingManager = new MockBindingManager();
+ ChildProcessLauncher.setBindingManagerForTesting(mBindingManager);
+ }
+
+ /**
+ * @return the index of the given tab in the current tab model
+ */
+ private int indexOf(Tab tab) {
+ return getActivity().getCurrentTabModel().indexOf(tab);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698