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

Unified Diff: chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.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/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
diff --git a/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java b/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c5d6b4d101007ff40dc0d718b01d2258752ef2c
--- /dev/null
+++ b/chrome/test/android/javatests_staging/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
@@ -0,0 +1,1068 @@
+// 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.test;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.AppTask;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.PowerManager;
+import android.provider.Browser;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.ListView;
+
+import com.google.android.apps.chrome.R;
+
+import junit.framework.Assert;
+
+import org.chromium.base.PerfTraceEvent;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.base.test.BaseActivityInstrumentationTestCase;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.PerfTest;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeMobileApplication;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.DeferredStartupHandler;
+import org.chromium.chrome.browser.EmptyTabObserver;
+import org.chromium.chrome.browser.Tab;
+import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.document.DocumentActivity;
+import org.chromium.chrome.browser.document.DocumentMetricIds;
+import org.chromium.chrome.browser.document.DocumentUtils;
+import org.chromium.chrome.browser.document.IncognitoDocumentActivity;
+import org.chromium.chrome.browser.infobar.InfoBar;
+import org.chromium.chrome.browser.ntp.NewTabPage;
+import org.chromium.chrome.browser.omaha.OmahaClient;
+import org.chromium.chrome.browser.omnibox.AutocompleteController;
+import org.chromium.chrome.browser.omnibox.LocationBarLayout;
+import org.chromium.chrome.browser.omnibox.OmniboxResultsAdapter.OmniboxResultItem;
+import org.chromium.chrome.browser.omnibox.OmniboxSuggestion;
+import org.chromium.chrome.browser.omnibox.UrlBar;
+import org.chromium.chrome.browser.preferences.NetworkPredictionOptions;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.preferences.Preferences;
+import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.chrome.test.util.ActivityUtils;
+import org.chromium.chrome.test.util.ApplicationData;
+import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.MenuUtils;
+import org.chromium.chrome.test.util.NewTabPageTestUtils;
+import org.chromium.chrome.test.util.OmniboxTestUtils;
+import org.chromium.chrome.test.util.TestHttpServerClient;
+import org.chromium.content.browser.test.util.CallbackHelper;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content.browser.test.util.JavaScriptUtils;
+import org.chromium.content.browser.test.util.KeyUtils;
+import org.chromium.content.browser.test.util.RenderProcessLimit;
+import org.chromium.content.browser.test.util.TestTouchUtils;
+import org.chromium.content.browser.test.util.TouchCommon;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.base.PageTransition;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Base class for all Chrome instrumentation tests.
+ * All tests must inherit from this class and define their own test methods
+ * See ChromeTabbedActivityTestBase.java for example.
+ * @param <T> A {@link ChromeActivity} class
+ */
+@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
+public abstract class ChromeActivityTestCaseBase<T extends ChromeActivity>
+ extends BaseActivityInstrumentationTestCase<T> {
+
+ private static final String TAG = "ChromeActivityTestCaseBase";
+
+ // The number of ms to wait for the rendering activity to be started.
+ protected static final int ACTIVITY_START_TIMEOUT_MS = 1000;
+
+ private static final String PERF_NORUN_TAG = "--NORUN--";
+
+ private static final String PERF_ANNOTATION_FORMAT = "**PERFANNOTATION(%s):";
+
+ private static final String MEMORY_TRACE_GRAPH_SUFFIX = " - browser PSS";
+
+ private static final String PERF_OUTPUT_FILE = "PerfTestData.txt";
+
+ private static final long OMNIBOX_FIND_SUGGESTION_TIMEOUT_MS = 10 * 1000;
+
+ public ChromeActivityTestCaseBase(Class<T> activityClass) {
+ super(activityClass);
+ }
+
+ protected boolean mSkipClearAppData = false;
+ private PowerManager.WakeLock mWakeLock = null;
+ protected boolean mSkipCheckHttpServer = false;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ setActivityInitialTouchMode(false);
+ if (!mSkipClearAppData) {
+ // We shouldn't clear the data at the end of test, it is needed for debugging.
+ assertTrue("Unable to clear the app data", clearAppData());
+ if (FeatureUtilities.isDocumentMode(getInstrumentation().getTargetContext())) {
+ closeAllChromeActivityAppTasks();
+ }
+ }
+ // Make sure the screen is on during test runs.
+ PowerManager pm = (PowerManager) getInstrumentation().getTargetContext()
+ .getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
+ | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, TAG);
+ mWakeLock.acquire();
+
+ // Disable Omaha related activities.
+ OmahaClient.setEnableCommunication(false);
+ OmahaClient.setEnableUpdateDetection(false);
+
+ if (!mSkipCheckHttpServer) {
+ TestHttpServerClient.checkServerIsUp();
+ }
+ startMainActivity();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ assertNotNull("Uninitialized wake lock", mWakeLock);
+ mWakeLock.release();
+ super.tearDown();
+ }
+
+ /**
+ * Called to start the Main Activity, the subclass should implemented with it desired start
+ * method.
+ * TODO: Make startMainActivityFromLauncher the default.
+ */
+ public abstract void startMainActivity() throws InterruptedException;
+
+ /**
+ * Matches testString against baseString.
+ * Returns 0 if there is no match, 1 if an exact match and 2 if a fuzzy match.
+ */
+ protected static int matchUrl(String baseString, String testString) {
+ if (baseString.equals(testString)) {
+ return 1;
+ }
+ if (baseString.contains(testString)) {
+ return 2;
+ }
+ return 0;
+ }
+
+ /**
+ * Invokes {@link Instrumentation#startActivitySync(Intent)} and sets the
+ * test case's activity to the result. See the documentation for
+ * {@link Instrumentation#startActivitySync(Intent)} on the timing of the
+ * return, but generally speaking the activity's "onCreate" has completed
+ * and the activity's main looper has become idle.
+ */
+ protected void startActivityCompletely(Intent intent) {
+ final Class<?> activityClazz =
+ FeatureUtilities.isDocumentMode(getInstrumentation().getTargetContext())
+ ? DocumentActivity.class : ChromeTabbedActivity.class;
+ Instrumentation.ActivityMonitor monitor = getInstrumentation().addMonitor(
+ activityClazz.getName(), null, false);
+ Activity activity = getInstrumentation().startActivitySync(intent);
+ assertNotNull("Main activity did not start", activity);
+ ChromeActivity chromeActivity = (ChromeActivity)
+ monitor.waitForActivityWithTimeout(ACTIVITY_START_TIMEOUT_MS);
+ assertNotNull("ChromeActivity did not start", chromeActivity);
+ setActivity(chromeActivity);
+ Log.d(TAG, "startActivityCompletely <<");
+ }
+
+ /**
+ * Clear all files and folders in the Chrome application directory except 'lib'.
+ *
+ * The 'cache' directory is recreated as an empty directory.
+ *
+ * @return Whether clearing the application data was successful.
+ */
+ protected boolean clearAppData() throws InterruptedException {
+ return ApplicationData.clearAppData(getInstrumentation().getTargetContext());
+ }
+
+ /**
+ * Closes all Chrome activity app tasks. This is for cleaning up Chrome tasks in the recent,
+ * those are not necessarily associated with a live activity.
+ */
+ private void closeAllChromeActivityAppTasks() throws ClassNotFoundException {
+ ActivityManager am = (ActivityManager) getInstrumentation().getTargetContext()
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
+ List<AppTask> taskList = am.getAppTasks();
+ for (AppTask task : taskList) {
+ String className = DocumentUtils.getTaskClassName(task, pm);
+ if (ChromeActivity.class.isAssignableFrom(Class.forName(className))) {
+ task.finishAndRemoveTask();
+ }
+ }
+ }
+
+ /**
+ * Lets tests specify whether they want prerendering turned on.
+ * It is on by default. Since in some places different code paths are used for the same feature
+ * depending of whether instant is on or off (ex: infobars), it is necessary for some tests to
+ * test with and without instant.
+ *
+ * @param enabled whether prerender should be on.
+ */
+ protected void setAllowPrerender(final boolean enabled) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ PrefServiceBridge.getInstance().setNetworkPredictionOptions(enabled
+ ? NetworkPredictionOptions.NETWORK_PREDICTION_ALWAYS
+ : NetworkPredictionOptions.NETWORK_PREDICTION_NEVER);
+ }
+ });
+ }
+
+ /**
+ * Starts (synchronously) a drag motion. Normally followed by dragTo() and dragEnd().
+ *
+ * @param x
+ * @param y
+ * @param downTime (in ms)
+ * @see TestTouchUtils
+ */
+ protected void dragStart(float x, float y, long downTime) {
+ TouchCommon.dragStart(getActivity(), x, y, downTime);
+ }
+
+ /**
+ * Drags / moves (synchronously) to the specified coordinates. Normally preceeded by
+ * dragStart() and followed by dragEnd()
+ *
+ * @param fromX
+ * @param toX
+ * @param fromY
+ * @param toY
+ * @param stepCount
+ * @param downTime (in ms)
+ * @see TestTouchUtils
+ */
+ protected void dragTo(float fromX, float toX, float fromY,
+ float toY, int stepCount, long downTime) {
+ TouchCommon.dragTo(getActivity(), fromX, toX, fromY, toY, stepCount, downTime);
+ }
+
+ /**
+ * Finishes (synchronously) a drag / move at the specified coordinate.
+ * Normally preceeded by dragStart() and dragTo().
+ *
+ * @param x
+ * @param y
+ * @param downTime (in ms)
+ * @see TestTouchUtils
+ */
+ protected void dragEnd(float x, float y, long downTime) {
+ TouchCommon.dragEnd(getActivity(), x, y, downTime);
+ }
+
+ /**
+ * Sends (synchronously) a single click to an absolute screen coordinates.
+ *
+ * @param x screen absolute
+ * @param y screen absolute
+ * @see TestTouchUtils
+ */
+ public void singleClick(float x, float y) {
+ TouchCommon.singleClick(getActivity(), x, y);
+ }
+
+ /**
+ * Sends (synchronously) a single click to the View at the specified coordinates.
+ *
+ * <p>
+ * Differs from
+ * {@link TestTouchUtils#singleClickView(android.app.Instrumentation, View, int, int)}
+ * as this does not rely on injecting events into the different activity. Injecting events has
+ * been unreliable for us and simulating the touch events in this manner is just as effective.
+ *
+ * @param v The view to be clicked.
+ * @param x Relative x location to v
+ * @param y Relative y location to v
+ */
+ public void singleClickView(View v, int x, int y) {
+ TouchCommon.singleClickView(v, x, y);
+ }
+
+ /**
+ * Sends (synchronously) a single click to the center of the View.
+ *
+ * <p>
+ * Differs from
+ * {@link TestTouchUtils#singleClickView(android.app.Instrumentation, View)}
+ * as this does not rely on injecting events into the different activity. Injecting events has
+ * been unreliable for us and simulating the touch events in this manner is just as effective.
+ *
+ * @param v The view to be clicked.
+ */
+ public void singleClickView(View v) {
+ TouchCommon.singleClickView(v);
+ }
+
+ /**
+ * Waits for {@link AsyncTask}'s that have been queued to finish. Note, this
+ * only waits for tasks that have been started using the default
+ * {@link java.util.concurrent.Executor}, which executes tasks serially.
+ *
+ * @param timeout how long to wait for tasks to complete
+ */
+ public void waitForAsyncTasks(long timeout) throws InterruptedException {
+ final Semaphore s = new Semaphore(0);
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... arg0) {
+ s.release();
+ return null;
+ }
+ }.execute();
+ assertTrue(s.tryAcquire(timeout, TimeUnit.MILLISECONDS));
+ }
+
+ /**
+ * Navigates to a URL directly without going through the UrlBar. This bypasses the page
+ * preloading mechanism of the UrlBar.
+ * @param url The url to load in the current tab.
+ * @return FULL_PRERENDERED_PAGE_LOAD or PARTIAL_PRERENDERED_PAGE_LOAD if the page has been
+ * prerendered. DEFAULT_PAGE_LOAD if it had not.
+ */
+ public int loadUrl(final String url) throws IllegalArgumentException, InterruptedException {
+ return loadUrlInTab(url, PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR,
+ getActivity().getActivityTab());
+ }
+
+ /**
+ * @param url The url of the page to load.
+ * @param pageTransition The type of transition. see
+ * {@link org.chromium.content.browser.PageTransition}
+ * for valid values.
+ * @param tab The tab to load the url into.
+ * @return FULL_PRERENDERED_PAGE_LOAD or PARTIAL_PRERENDERED_PAGE_LOAD if the
+ * page has been prerendered. DEFAULT_PAGE_LOAD if it had not.
+ */
+ public int loadUrlInTab(final String url, final int pageTransition, final Tab tab)
+ throws InterruptedException {
+ assertNotNull("Cannot load the url in a null tab", tab);
+ final AtomicInteger result = new AtomicInteger();
+
+ ChromeTabUtils.waitForTabPageLoaded(tab, new Runnable() {
+ @Override
+ public void run() {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ result.set(tab.loadUrl(
+ new LoadUrlParams(url, pageTransition)));
+ }
+ });
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ return result.get();
+ }
+
+ /**
+ * Load a url in a new tab. The {@link Tab} will pretend to be created from a link.
+ * @param url The url of the page to load.
+ */
+ public void loadUrlInNewTab(final String url) throws InterruptedException {
+ // TODO(mariakhomenko): There is no current tab creator in document mode, will need
+ // additional logic here for Document tests.
+ if (FeatureUtilities.isDocumentMode(getInstrumentation().getContext())) {
+ fail("Document mode not yet supported.");
+ }
+ try {
+ Tab tab = ThreadUtils.runOnUiThreadBlocking(new Callable<Tab>() {
+ @Override
+ public Tab call() throws Exception {
+ return getActivity().getCurrentTabCreator()
+ .launchUrl(url, TabLaunchType.FROM_LINK);
+ }
+ });
+
+ ChromeTabUtils.waitForTabPageLoaded(tab, url);
+ getInstrumentation().waitForIdleSync();
+ } catch (ExecutionException e) {
+ fail("Failed to create new tab");
+ }
+ }
+
+ /**
+ * Load a url in a new tab. The {@link Tab} will pretend to be created from a link.
+ * @param url The url of the page to load.
+ * @param incognito Whether the new tab should be incognito.
+ */
+ public void loadUrlInNewTab(final String url, final boolean incognito)
+ throws InterruptedException {
+ Tab tab = null;
+ if (FeatureUtilities.isDocumentMode(getInstrumentation().getContext())) {
+ Runnable activityTrigger = new Runnable() {
+ @Override
+ public void run() {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ ChromeLauncherActivity.launchDocumentInstance(getActivity(), incognito,
+ ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, url,
+ DocumentMetricIds.STARTED_BY_UNKNOWN,
+ PageTransition.AUTO_TOPLEVEL,
+ false, null);
+ }
+ });
+ }
+ };
+ final DocumentActivity activity = ActivityUtils.waitForActivity(
+ getInstrumentation(),
+ incognito ? IncognitoDocumentActivity.class : DocumentActivity.class,
+ activityTrigger);
+ CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return activity.getActivityTab() != null;
+ }
+ });
+ tab = activity.getActivityTab();
+ } else {
+ try {
+ tab = ThreadUtils.runOnUiThreadBlocking(new Callable<Tab>() {
+ @Override
+ public Tab call() throws Exception {
+ return getActivity().getTabCreator(incognito)
+ .launchUrl(url, TabLaunchType.FROM_LINK);
+ }
+ });
+ } catch (ExecutionException e) {
+ fail("Failed to create new tab");
+ }
+ }
+ ChromeTabUtils.waitForTabPageLoaded(tab, url);
+ getInstrumentation().waitForIdleSync();
+ }
+
+ /**
+ * Simulates starting Main Activity from launcher, blocks until it is started.
+ */
+ protected void startMainActivityFromLauncher() throws InterruptedException {
+ startMainActivityWithURL(null);
+ }
+
+ /**
+ * Starts the Main activity on the specified URL. Passing a null URL ensures the default page is
+ * loaded, which is the NTP with a new profile .
+ */
+ protected void startMainActivityWithURL(String url) throws InterruptedException {
+ // Only launch Chrome.
+ Intent intent = new Intent(
+ TextUtils.isEmpty(url) ? Intent.ACTION_MAIN : Intent.ACTION_VIEW);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ startMainActivityFromIntent(intent, url);
+ }
+
+ /**
+ * Starts the Main activity and open a blank page.
+ * This is faster and less flakyness-prone than starting on the NTP.
+ */
+ protected void startMainActivityOnBlankPage() throws InterruptedException {
+ startMainActivityWithURL("about:blank");
+ }
+
+ /**
+ * Starts the Main activity as if it was started from an external application, on the specified
+ * URL.
+ */
+ protected void startMainActivityFromExternalApp(String url, String appId)
+ throws InterruptedException {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ if (appId != null) {
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, appId);
+ }
+ startMainActivityFromIntent(intent, url);
+ }
+
+ /**
+ * Starts the Main activity using the passed intent, and using the specified URL.
+ * This method waits for DEFERRED_STARTUP to fire as well as a subsequent
+ * idle-sync of the main looper thread, and the initial tab must either
+ * complete its load or it must crash before this method will return.
+ */
+ protected void startMainActivityFromIntent(Intent intent, String url)
+ throws InterruptedException {
+ prepareUrlIntent(intent, url);
+
+ final boolean isDocumentMode =
+ FeatureUtilities.isDocumentMode(getInstrumentation().getContext());
+
+ startActivityCompletely(intent);
+
+ assertTrue("Tab never selected/initialized.",
+ CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return getActivity().getActivityTab() != null;
+ }
+ }));
+ Tab tab = getActivity().getActivityTab();
+
+ ChromeTabUtils.waitForTabPageLoaded(tab, (String) null);
+
+ if (!isDocumentMode && tab != null && NewTabPage.isNTPUrl(tab.getUrl())) {
+ boolean ntpReady = NewTabPageTestUtils.waitForNtpLoaded(tab);
+ if (!ntpReady && tab.isShowingSadTab()) {
+ fail("Renderer crashed before NTP finished loading. "
+ + "Look at logcat for renderer stack dump.");
+ }
+ assertTrue("Initial NTP never fully loaded.", ntpReady);
+ }
+
+ assertTrue("Deferred startup never completed",
+ CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return DeferredStartupHandler.getInstance().isDeferredStartupComplete();
+ }
+ }));
+
+ assertNotNull(tab);
+ assertNotNull(tab.getView());
+ getInstrumentation().waitForIdleSync();
+ }
+
+ /**
+ * Prepares a URL intent to start the activity.
+ * @param intent the intent to be modified
+ * @param url the URL to be used (may be null)
+ */
+ protected Intent prepareUrlIntent(Intent intent, String url) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setComponent(new ComponentName(getInstrumentation().getTargetContext(),
+ ChromeLauncherActivity.class));
+
+ if (url != null) {
+ intent.setData(Uri.parse(url));
+ }
+
+ try {
+ Method method = getClass().getMethod(getName(), (Class[]) null);
+ if (method.isAnnotationPresent(RenderProcessLimit.class)) {
+ RenderProcessLimit limit = method.getAnnotation(RenderProcessLimit.class);
+ intent.putExtra(ChromeTabbedActivity.INTENT_EXTRA_TEST_RENDER_PROCESS_LIMIT,
+ limit.value());
+ }
+ } catch (Exception ex) {
+ // Ignore exception.
+ }
+ return intent;
+ }
+
+ /**
+ * Open an incognito tab by invoking the 'new incognito' menu item.
+ * Returns when receiving the 'PAGE_LOAD_FINISHED' notification.
+ */
+ protected void newIncognitoTabFromMenu() throws InterruptedException {
+ Tab tab = null;
+
+ if (FeatureUtilities.isDocumentMode(getInstrumentation().getContext())) {
+ final IncognitoDocumentActivity activity = ActivityUtils.waitForActivity(
+ getInstrumentation(), IncognitoDocumentActivity.class,
+ new Runnable() {
+ @Override
+ public void run() {
+ MenuUtils.invokeCustomMenuActionSync(
+ getInstrumentation(), getActivity(),
+ R.id.new_incognito_tab_menu_id);
+ }
+ });
+
+ CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return activity.getActivityTab() != null;
+ }
+ });
+
+ tab = activity.getActivityTab();
+ } else {
+ final CallbackHelper createdCallback = new CallbackHelper();
+ final CallbackHelper selectedCallback = new CallbackHelper();
+
+ TabModel incognitoTabModel = getActivity().getTabModelSelector().getModel(true);
+ TabModelObserver observer = new EmptyTabModelObserver() {
+ @Override
+ public void didAddTab(Tab tab, TabLaunchType type) {
+ createdCallback.notifyCalled();
+ }
+
+ @Override
+ public void didSelectTab(Tab tab, TabSelectionType type, int lastId) {
+ selectedCallback.notifyCalled();
+ }
+ };
+ incognitoTabModel.addObserver(observer);
+
+ MenuUtils.invokeCustomMenuActionSync(getInstrumentation(), getActivity(),
+ R.id.new_incognito_tab_menu_id);
+
+ try {
+ createdCallback.waitForCallback(0);
+ } catch (TimeoutException ex) {
+ fail("Never received tab created event");
+ }
+ try {
+ selectedCallback.waitForCallback(0);
+ } catch (TimeoutException ex) {
+ fail("Never received tab selected event");
+ }
+ incognitoTabModel.removeObserver(observer);
+
+ tab = getActivity().getActivityTab();
+ }
+
+ ChromeTabUtils.waitForTabPageLoaded(tab, (String) null);
+ Assert.assertTrue("NTP never fully loaded.",
+ NewTabPageTestUtils.waitForNtpLoaded(tab));
+ getInstrumentation().waitForIdleSync();
+ Log.d(TAG, "newIncognitoTabFromMenu <<");
+ }
+
+ /**
+ * New multiple incognito tabs by invoking the 'new incognito' menu item n times.
+ * @param n The number of tabs you want to create.
+ */
+ protected void newIncognitoTabsFromMenu(int n)
+ throws InterruptedException {
+ while (n > 0) {
+ newIncognitoTabFromMenu();
+ --n;
+ }
+ }
+
+ /**
+ * @return The number of incognito tabs currently open.
+ */
+ protected int incognitoTabsCount() {
+ return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Integer>() {
+ @Override
+ public Integer call() {
+ TabModelSelector tabModelSelector;
+ if (FeatureUtilities.isDocumentMode(getInstrumentation().getContext())) {
+ tabModelSelector = ChromeMobileApplication.getDocumentTabModelSelector();
+ } else {
+ tabModelSelector = getActivity().getTabModelSelector();
+ }
+ return tabModelSelector.getModel(true).getCount();
+ }
+ });
+ }
+
+ /**
+ * Types the passed text in the omnibox to trigger a navigation. You can pass a URL or a search
+ * term.
+ * <p>
+ * Note that this code triggers suggestions and prerendering. Unless you are testing these
+ * features specifically, you should use loadUrl() which is less prone to flakyness.
+ * @param url The Url to navigate to.
+ * @return the url in the UrlBar.
+ * @throws InterruptedException
+ */
+ public String typeInOmniboxAndNavigate(final String url) throws InterruptedException {
+ final UrlBar urlBar = (UrlBar) getActivity().findViewById(R.id.url_bar);
+ assertNotNull("urlBar is null", urlBar);
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ urlBar.requestFocus();
+ urlBar.setText(url);
+ }
+ });
+ final LocationBarLayout locationBar =
+ (LocationBarLayout) getActivity().findViewById(R.id.location_bar);
+ assertTrue("Omnibox Suggestions never shown.",
+ OmniboxTestUtils.waitForOmniboxSuggestions(locationBar));
+
+ Tab currentTab = getActivity().getActivityTab();
+ final CallbackHelper loadedCallback = new CallbackHelper();
+ final AtomicBoolean tabCrashReceived = new AtomicBoolean();
+ currentTab.addObserver(new EmptyTabObserver() {
+ @Override
+ public void onPageLoadFinished(Tab tab) {
+ loadedCallback.notifyCalled();
+ tab.removeObserver(this);
+ }
+
+ @Override
+ public void onCrash(Tab tab, boolean sadTabShown) {
+ tabCrashReceived.set(true);
+ tab.removeObserver(this);
+ }
+ });
+
+ // Loads the url.
+ KeyUtils.singleKeyEventView(getInstrumentation(), urlBar, KeyEvent.KEYCODE_ENTER);
+
+ boolean pageLoadReceived = true;
+ try {
+ loadedCallback.waitForCallback(0);
+ } catch (TimeoutException ex) {
+ pageLoadReceived = false;
+ }
+
+ assertTrue("Neither PAGE_LOAD_FINISHED nor a TAB_CRASHED event was received",
+ pageLoadReceived || tabCrashReceived.get());
+ getInstrumentation().waitForIdleSync();
+
+ // The title has been set before the page notification was broadcast, so it is safe to
+ // access the title.
+ return urlBar.getText().toString();
+ }
+
+ /**
+ * Looks up the Omnibox in the view hierarchy and types the specified
+ * text into it, requesting focus and using an inter-character delay of
+ * 200ms.
+ *
+ * @param oneCharAtATime Whether to type text one character at a time or all at once.
+ *
+ * @throws InterruptedException
+ */
+ public void typeInOmnibox(final String text, final boolean oneCharAtATime)
+ throws InterruptedException {
+ final UrlBar urlBar = (UrlBar) getActivity().findViewById(R.id.url_bar);
+ assertNotNull(urlBar);
+
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ urlBar.requestFocus();
+ if (!oneCharAtATime) {
+ urlBar.setText(text);
+ }
+ }
+ });
+
+ if (oneCharAtATime) {
+ final Instrumentation instrumentation = getInstrumentation();
+ for (int i = 0; i < text.length(); ++i) {
+ instrumentation.sendStringSync(text.substring(i, i + 1));
+ // Let's put some delay between key strokes to simulate a user pressing the keys.
+ Thread.sleep(20);
+ }
+ }
+ }
+
+ /**
+ * Searches for a given suggestion after typing given text in the Omnibox.
+ *
+ * @param inputText Input text to type into the Omnibox.
+ * @param displayText Suggestion text expected to be found. Passing in null ignores this field.
+ * @param url URL expected to be found. Passing in null ignores this field.
+ * @param type Type of suggestion expected to be found. Passing in null ignores this field.
+ *
+ * @throws InterruptedException
+ */
+ protected OmniboxSuggestion findOmniboxSuggestion(String inputText, String displayText,
+ String url, OmniboxSuggestion.Type type) throws InterruptedException {
+ long endTime = System.currentTimeMillis() + OMNIBOX_FIND_SUGGESTION_TIMEOUT_MS;
+
+ // Multiple suggestion events may occur before the one we're interested in is received.
+ final CallbackHelper onSuggestionsReceivedHelper = new CallbackHelper();
+ final LocationBarLayout locationBar =
+ (LocationBarLayout) getActivity().findViewById(R.id.location_bar);
+ locationBar.setAutocompleteController(new AutocompleteController(locationBar) {
+ @Override
+ public void onSuggestionsReceived(
+ List<OmniboxSuggestion> suggestions,
+ String inlineAutocompleteText,
+ long currentNativeAutocompleteResult) {
+ super.onSuggestionsReceived(
+ suggestions, inlineAutocompleteText, currentNativeAutocompleteResult);
+ onSuggestionsReceivedHelper.notifyCalled();
+ }
+ });
+
+ try {
+ typeInOmnibox(inputText, false);
+
+ while (true) {
+ try {
+ int callbackCount = onSuggestionsReceivedHelper.getCallCount();
+ onSuggestionsReceivedHelper.waitForCallback(
+ callbackCount, 1,
+ endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+ } catch (TimeoutException exception) {
+ return null;
+ }
+
+ // Wait for suggestions to show up.
+ assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return ((LocationBarLayout) getActivity().findViewById(
+ R.id.location_bar)).getSuggestionList() != null;
+ }
+ }, 3000, 10));
+ final ListView suggestionListView = locationBar.getSuggestionList();
+ OmniboxResultItem popupItem = (OmniboxResultItem) suggestionListView
+ .getItemAtPosition(0);
+ OmniboxSuggestion suggestion = popupItem.getSuggestion();
+ if (suggestionListView.getCount() == 1
+ && suggestion.getDisplayText().equals(inputText)
+ && !suggestion.getDisplayText().equals(displayText)) {
+ // If there is only one suggestion and it's the same as inputText,
+ // wait for other suggestions before looking for the one we want.
+ CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return suggestionListView.getCount() > 1;
+ }
+ }, 3000, 10);
+ }
+ int count = suggestionListView.getCount();
+ for (int i = 0; i < count; i++) {
+ popupItem = (OmniboxResultItem) suggestionListView.getItemAtPosition(i);
+ suggestion = popupItem.getSuggestion();
+ if (type != null && suggestion.getType() != type) {
+ continue;
+ }
+ if (displayText != null && !suggestion.getDisplayText().equals(displayText)) {
+ continue;
+ }
+ if (url != null && !suggestion.getUrl().equals(url)) {
+ continue;
+ }
+ return suggestion;
+ }
+ }
+ } finally {
+ locationBar.setAutocompleteController(new AutocompleteController(locationBar));
+ }
+ }
+
+ /**
+ * Returns the infobars being displayed by the current tab, or null if they don't exist.
+ */
+ protected List<InfoBar> getInfoBars() {
+ Tab currentTab = getActivity().getActivityTab();
+ if (currentTab == null) {
+ return null;
+ }
+
+ if (currentTab.getInfoBarContainer() != null) {
+ return currentTab.getInfoBarContainer().getInfoBars();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Launches the preferences menu and starts the preferences activity named fragmentName.
+ * Returns the activity that was started.
+ */
+ protected Preferences startPreferences(String fragmentName) {
+ Context context = getInstrumentation().getTargetContext();
+ Intent intent = PreferencesLauncher.createIntentForSettingsPage(context, fragmentName);
+ Activity activity = getInstrumentation().startActivitySync(intent);
+ assertTrue(activity instanceof Preferences);
+ return (Preferences) activity;
+ }
+
+ /**
+ * Executes the given snippet of JavaScript code within the current tab. Returns the result of
+ * its execution in JSON format.
+ * @throws InterruptedException
+ */
+ protected String runJavaScriptCodeInCurrentTab(String code) throws InterruptedException,
+ TimeoutException {
+ return JavaScriptUtils.executeJavaScriptAndWaitForResult(
+ getActivity().getCurrentContentViewCore().getWebContents(), code);
+ }
+
+ @Override
+ protected void runTest() throws Throwable {
+ boolean shouldRun = true;
+ String perfTagAnalysisString = "";
+ try {
+ shouldRun = RestrictedInstrumentationTestCase.shouldRunTest(this);
+ perfTagAnalysisString = setupPotentialPerfTest(shouldRun);
+ } catch (Exception e) {
+ // eat the exception here; super.runTest() will catch it again and handle it properly
+ }
+
+ if (shouldRun) super.runTest();
+
+ endPerfTest(perfTagAnalysisString);
+ }
+
+ /**
+ * Waits till the ContentViewCore receives the expected page scale factor
+ * from the compositor and asserts that this happens.
+ *
+ * Upstream {@code ContentShellTestBase} has the same copy. Also, this is a temporary solution
+ * for waiting a page load. Please refer to the bug at the upstream function.
+ */
+ protected void assertWaitForPageScaleFactorMatch(final float expectedScale)
+ throws InterruptedException {
+ boolean scaleFactorMatch = CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return getActivity().getCurrentContentViewCore().getScale()
+ == expectedScale;
+ }
+ });
+ assertTrue("Expecting scale factor of: " + expectedScale + ", got: "
+ + getActivity().getCurrentContentViewCore().getScale(), scaleFactorMatch);
+ }
+
+ /**
+ * This method creates a special string that tells the python test harness what
+ * trace calls to track for this particular test run. It can support multiple trace calls for
+ * each test and will make a new graph entry for all of them. It should be noted that this
+ * method eats all exceptions. This is so that it can never be the cause of a test failure.
+ * We still need to call this method even if we know the test will not run (ie: willTestRun is
+ * false). This is because this method lets the python test harness know not to expect any
+ * perf output in this case. In the case that the autoTrace parameter is set for the current
+ * test method, this will also start the PerfTrace facility automatically.
+ *
+ * @param willTestRun Whether or not this test will actually be run.
+ * @return A specially formatted string that contains which JSON perf markers to look at. This
+ * will be analyzed by the perf test harness.
+ */
+ @SuppressFBWarnings({
+ "REC_CATCH_EXCEPTION",
+ "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE",
+ })
+ private String setupPotentialPerfTest(boolean willTestRun) {
+ File perfFile = getInstrumentation().getTargetContext().getFileStreamPath(
+ PERF_OUTPUT_FILE);
+ perfFile.delete();
+ PerfTraceEvent.setOutputFile(perfFile);
+
+ String perfAnnotationString = "";
+
+ try {
+ Method method = getClass().getMethod(getName(), (Class[]) null);
+ PerfTest annotation = method.getAnnotation(PerfTest.class);
+ if (annotation != null) {
+ StringBuilder annotationData = new StringBuilder();
+ annotationData.append(String.format(PERF_ANNOTATION_FORMAT, method.getName()));
+
+ if (!willTestRun) {
+ annotationData.append(PERF_NORUN_TAG);
+ } else {
+ // Grab the minimum number of trace calls we will track (if names(),
+ // graphNames(), and graphValues() do not have the same number of elements, we
+ // will track as many as we can given the data available.
+ final int maxIndex = Math.min(annotation.traceNames().length, Math.min(
+ annotation.graphNames().length, annotation.seriesNames().length));
+
+ List<String> allNames = new LinkedList<String>();
+ for (int i = 0; i < maxIndex; ++i) {
+ // Prune out all of ',' and ';' from the strings. Replace them with '-'.
+ String name = annotation.traceNames()[i].replaceAll("[,;]", "-");
+ allNames.add(name);
+ String graphName = annotation.graphNames()[i].replaceAll("[,;]", "-");
+ String seriesName = annotation.seriesNames()[i].replaceAll("[,;]", "-");
+ if (annotation.traceTiming()) {
+ annotationData.append(name).append(",")
+ .append(graphName).append(",")
+ .append(seriesName).append(';');
+ }
+
+ // If memory tracing is enabled, add an additional graph for each one
+ // defined to track timing perf that will track the corresponding memory
+ // usage.
+ // Keep the series name the same, but just append a memory identifying
+ // prefix to the graph.
+ if (annotation.traceMemory()) {
+ String memName = PerfTraceEvent.makeMemoryTraceNameFromTimingName(name);
+ String memGraphName = PerfTraceEvent.makeSafeTraceName(
+ graphName, MEMORY_TRACE_GRAPH_SUFFIX);
+ annotationData.append(memName).append(",")
+ .append(memGraphName).append(",")
+ .append(seriesName).append(';');
+ allNames.add(memName);
+ }
+ }
+ // We only record perf trace events for the names explicitly listed.
+ PerfTraceEvent.setFilter(allNames);
+
+ // Figure out if we should automatically start or stop the trace.
+ if (annotation.autoTrace()) {
+ PerfTraceEvent.setEnabled(true);
+ }
+ PerfTraceEvent.setTimingTrackingEnabled(annotation.traceTiming());
+ PerfTraceEvent.setMemoryTrackingEnabled(annotation.traceMemory());
+ }
+
+ perfAnnotationString = annotationData.toString();
+ }
+ } catch (Exception ex) {
+ // Eat exception here.
+ }
+
+ return perfAnnotationString;
+ }
+
+ /**
+ * This handles cleaning up the performance component of this test if it was a UI Perf test.
+ * This includes potentially shutting down PerfTraceEvent. This method eats all exceptions so
+ * that it can never be the cause of a test failure. The test harness will wait for
+ * {@code perfTagAnalysisString} to show up in the logcat before processing the JSON perf file,
+ * giving this method the chance to flush and dump the performance data before the harness reads
+ * it.
+ *
+ * @param perfTagAnalysisString A specially formatted string that tells the perf test harness
+ * which perf tags to analyze.
+ */
+ private void endPerfTest(String perfTagAnalysisString) {
+ try {
+ Method method = getClass().getMethod(getName(), (Class[]) null);
+ PerfTest annotation = method.getAnnotation(PerfTest.class);
+ if (annotation != null) {
+ if (PerfTraceEvent.enabled()) {
+ PerfTraceEvent.setEnabled(false);
+ }
+
+ System.out.println(perfTagAnalysisString);
+ }
+ } catch (Exception ex) {
+ // Eat exception here.
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698