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

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

Issue 2847263003: Add tests to make sure closed CCT activities are not leaked. (Closed)
Patch Set: Revert debugging Created 3 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/customtabs/CustomTabActivityTest.java
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 83646723b829140df357fbf683bc80289cfd8dd5..3ee9a35621bf3cfbc253f5354ee8c945198e2d51 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -32,6 +32,7 @@ import android.support.customtabs.CustomTabsService;
import android.support.customtabs.CustomTabsServiceConnection;
import android.support.customtabs.CustomTabsSession;
import android.support.customtabs.CustomTabsSessionToken;
+import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
import android.support.test.filters.SmallTest;
import android.text.TextUtils;
@@ -49,6 +50,7 @@ import org.chromium.base.ApplicationStatus.ActivityStateListener;
import org.chromium.base.ObserverList.RewindableIterator;
import org.chromium.base.PathUtils;
import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.SuppressFBWarnings;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.test.util.CallbackHelper;
@@ -68,6 +70,7 @@ import org.chromium.chrome.browser.appmenu.AppMenuHandler;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.firstrun.FirstRunStatus;
import org.chromium.chrome.browser.metrics.PageLoadMetrics;
+import org.chromium.chrome.browser.preferences.Preferences;
import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
@@ -79,6 +82,8 @@ import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.toolbar.CustomTabToolbar;
import org.chromium.chrome.browser.util.ColorUtils;
+import org.chromium.chrome.test.util.ApplicationTestUtils;
+import org.chromium.chrome.test.util.ApplicationTestUtils.ActivityCloser;
import org.chromium.chrome.test.util.ChromeRestriction;
import org.chromium.chrome.test.util.browser.LocationSettingsTestUtil;
import org.chromium.chrome.test.util.browser.contextmenu.ContextMenuUtils;
@@ -94,7 +99,10 @@ import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.net.test.util.TestWebServer;
import org.chromium.ui.mojom.WindowOpenDisposition;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -2350,6 +2358,144 @@ public class CustomTabActivityTest extends CustomTabActivityTestBase {
assertEquals(testUrl, tab.getUrl());
}
+ @LargeTest
+ public void testClosedActivitiesNotLeaked() throws Exception {
+ openCloseActivities(true /* closeFrontToBack */, new ActivityCloser<CustomTabActivity>() {
+ @Override
+ public void close(CustomTabActivity activity) {
+ CustomTabToolbar toolbar = (CustomTabToolbar) activity.findViewById(R.id.toolbar);
+ toolbar.getCloseButtonForTest().performClick();
+ }
+ });
+ }
+
+ @LargeTest
+ public void testClosedBackActivitiesNotLeaked() throws Exception {
+ openCloseActivities(true /* closeFrontToBack */, new ActivityCloser<CustomTabActivity>() {
+ @Override
+ public void close(CustomTabActivity activity) {
+ activity.onBackPressed();
+ }
+ });
+ }
+
+ @LargeTest
+ public void testFinishedActivitiesNotLeaked() throws Exception {
+ openCloseActivities(true /* closeFrontToBack */, new ActivityCloser<CustomTabActivity>() {
+ @Override
+ public void close(CustomTabActivity activity) {
+ activity.finish();
+ }
+ });
+ }
+
+ @LargeTest
+ public void testStoppedFinishedActivitiesNotLeaked() throws Exception {
+ openCloseActivities(false /* closeFrontToBack */, new ActivityCloser<CustomTabActivity>() {
+ @Override
+ public void close(CustomTabActivity activity) {
+ activity.finish();
+ }
+ });
+ }
+
+ /**
+ * Opens several activities, closes them, and checks that activities are GCed.
+ *
+ * @param closeFrontToBack Whether to close activities starting from the
+ * last opened one (front), or from the first opened one (back).
+ * Essentially this param controls whether activities are closed
+ * in the active (true) or stopped (false) state.
+ * @param activityCloser Activity closing logic.
+ */
+ @SuppressFBWarnings("DM_GC")
+ private void openCloseActivities(boolean closeFrontToBack,
+ final ActivityCloser<CustomTabActivity> activityCloser) throws Exception {
+ final int activityCount = 5;
+
+ final List<WeakReference<Activity>> activityRefs = new ArrayList<>();
+
+ // Something in ApplicationTestUtils.closeActivity() call below retains last closed
+ // activity (in a local reference). I couldn't figure out what it is, so to avoid
+ // the problem we start one extra activity so that it's closed last. We don't care
+ // what that extra activity is, as long it's not CustomTabActivity. Note that we
+ // need to start extra activity before / after CCT activities according to
+ // closeFrontToBack flag.
+ Runnable extraActivityStarter = new Runnable() {
+ @Override
+ public void run() {
+ Intent intent = new Intent();
+ intent.setClass(getInstrumentation().getTargetContext(), Preferences.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ activityRefs.add(
+ new WeakReference<>(getInstrumentation().startActivitySync(intent)));
+ }
+ };
+ ActivityCloser<Activity> extraActivityCloser = new ActivityCloser<Activity>() {
+ @Override
+ public void close(Activity activity) {
+ activity.finish();
+ }
+ };
+
+ // We're closing front to back, so extra activity should be started first.
+ if (closeFrontToBack) extraActivityStarter.run();
+
+ for (int i = 0; i != activityCount; ++i) {
+ // Don't use startActivityCompletely() because it retains the started
+ // activity in several member variables.
+ CustomTabActivity activity =
+ launchCustomTabActivityCompletely(createMinimalCustomTabIntent());
+ waitForCustomTab(activity);
+ activityRefs.add(new WeakReference<Activity>(activity));
+ }
+
+ // We're closing back to front, so extra activity should be started last.
+ if (!closeFrontToBack) extraActivityStarter.run();
+
+ // Make sure that the first activity we need to close is at the beginning.
+ if (closeFrontToBack) Collections.reverse(activityRefs);
+
+ for (WeakReference<Activity> ref : activityRefs) {
+ Activity activity = ref.get();
+ // Activity can be null here if "Don't keep activities" developer option
+ // is turned on.
+ if (activity != null) {
+ if (activity instanceof CustomTabActivity) {
+ ApplicationTestUtils.closeActivity(
+ (CustomTabActivity) activity, activityCloser);
+ } else {
+ // Must be an extra activity started above.
+ ApplicationTestUtils.closeActivity(activity, extraActivityCloser);
+ }
+ }
+ }
+
+ // GC the activities. Note that System.gc() is a no-op unless it is followed
+ // by or following a call to System.runFinalization().
+ System.gc();
+ System.runFinalization();
+ System.gc();
+
+ for (int i = 0; i != activityCount; ++i) {
+ WeakReference<Activity> ref = activityRefs.get(i);
+
+ // If this fails: something retains activities! It's probably a leak!
+ // In order to find the culprit:
+ // * Repro locally
+ // * Comment out the assert
+ // * Add Thread.sleep() for a minute or so
+ // * While it's sleeping, open DDMS and take an HPROF dump
+ // * Use ahat tool (go/ahat) to open the dump
+ // * Go to 'allocations' page
+ // * Find 'SeparateTaskCustomTabActivity' row
+ // * Click on the number, *not* on the class name
+ // * You will see a list of live SeparateTaskCustomTabActivity objects -
+ // click on entries and inspect their 'Sample Path from GC Root'.
+ assertNull("Activity is leaked", ref.get());
+ }
+ }
+
/** Maybe prerenders a URL with a referrer, then launch it with another one. */
private void maybeSpeculateAndLaunchWithReferrers(String url, int speculationMode,
String speculationReferrer, String launchReferrer) throws Exception {

Powered by Google App Engine
This is Rietveld 408576698