Merge "Makeshift analog of Strictmode leak detector" into ub-launcher3-rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
d73d39b45a
@@ -203,7 +203,7 @@ public class NavigationModeSwitchRule implements TestRule {
|
||||
+ launcher.getNavigationModeMismatchError(),
|
||||
() -> launcher.getNavigationModeMismatchError() == null,
|
||||
60000 /* b/148422894 */, launcher);
|
||||
AbstractLauncherUiTest.checkDetectedLeaks();
|
||||
AbstractLauncherUiTest.checkDetectedLeaks(launcher);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,9 +98,10 @@ public abstract class AbstractLauncherUiTest {
|
||||
public static final long DEFAULT_UI_TIMEOUT = 10000;
|
||||
private static final String TAG = "AbstractLauncherUiTest";
|
||||
|
||||
private static String sDetectedActivityLeak;
|
||||
private static String sStrictmodeDetectedActivityLeak;
|
||||
private static boolean sActivityLeakReported;
|
||||
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
|
||||
private static final ActivityLeakTracker ACTIVITY_LEAK_TRACKER = new ActivityLeakTracker();
|
||||
|
||||
protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
|
||||
protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
|
||||
@@ -113,30 +114,51 @@ public abstract class AbstractLauncherUiTest {
|
||||
if (TestHelpers.isInLauncherProcess()) {
|
||||
StrictMode.VmPolicy.Builder builder =
|
||||
new StrictMode.VmPolicy.Builder()
|
||||
.detectActivityLeaks()
|
||||
// b/154772063
|
||||
// .detectActivityLeaks()
|
||||
.penaltyLog()
|
||||
.penaltyListener(Runnable::run, violation -> {
|
||||
// Runs in the main thread. We can't dumpheap in the main thread,
|
||||
// so let's just mark the fact that the leak has happened.
|
||||
if (sDetectedActivityLeak == null) {
|
||||
sDetectedActivityLeak = violation.toString();
|
||||
try {
|
||||
Debug.dumpHprofData(
|
||||
getInstrumentation().getTargetContext()
|
||||
.getFilesDir().getPath()
|
||||
+ "/ActivityLeakHeapDump.hprof");
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "dumpHprofData failed", e);
|
||||
}
|
||||
if (sStrictmodeDetectedActivityLeak == null) {
|
||||
sStrictmodeDetectedActivityLeak = violation.toString() + ", "
|
||||
+ dumpHprofData() + ".";
|
||||
}
|
||||
});
|
||||
StrictMode.setVmPolicy(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkDetectedLeaks() {
|
||||
if (sDetectedActivityLeak != null && !sActivityLeakReported) {
|
||||
public static void checkDetectedLeaks(LauncherInstrumentation launcher) {
|
||||
if (sActivityLeakReported) return;
|
||||
|
||||
if (sStrictmodeDetectedActivityLeak != null) {
|
||||
// Report from the test thread strictmode violations detected in the main thread.
|
||||
sActivityLeakReported = true;
|
||||
Assert.fail(sStrictmodeDetectedActivityLeak);
|
||||
}
|
||||
|
||||
// Check whether activity leak detector has found leaked activities.
|
||||
Wait.atMost(AbstractLauncherUiTest::getActivityLeakErrorMessage,
|
||||
() -> {
|
||||
launcher.getTotalPssKb(); // Triggers GC
|
||||
return MAIN_EXECUTOR.submit(
|
||||
() -> ACTIVITY_LEAK_TRACKER.noLeakedActivities()).get();
|
||||
}, DEFAULT_UI_TIMEOUT, launcher);
|
||||
}
|
||||
|
||||
private static String getActivityLeakErrorMessage() {
|
||||
sActivityLeakReported = true;
|
||||
return "Activity leak detector has found leaked activities, " + dumpHprofData() + ".";
|
||||
}
|
||||
|
||||
private static String dumpHprofData() {
|
||||
try {
|
||||
final String fileName = getInstrumentation().getTargetContext().getFilesDir().getPath()
|
||||
+ "/ActivityLeakHeapDump.hprof";
|
||||
Debug.dumpHprofData(fileName);
|
||||
return "memory dump filename: " + fileName;
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "dumpHprofData failed", e);
|
||||
return "failed to save memory dump";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +285,7 @@ public abstract class AbstractLauncherUiTest {
|
||||
if (mLauncherPid != 0) {
|
||||
assertEquals("Launcher crashed, pid mismatch:", mLauncherPid, mLauncher.getPid());
|
||||
}
|
||||
checkDetectedLeaks();
|
||||
checkDetectedLeaks(mLauncher);
|
||||
}
|
||||
|
||||
protected void clearLauncherData() throws IOException, InterruptedException {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import com.android.launcher3.tapl.TestHelpers;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
class ActivityLeakTracker implements Application.ActivityLifecycleCallbacks {
|
||||
private final WeakHashMap<Activity, Boolean> mActivities = new WeakHashMap<>();
|
||||
|
||||
ActivityLeakTracker() {
|
||||
if (!TestHelpers.isInLauncherProcess()) return;
|
||||
final Application app =
|
||||
(Application) InstrumentationRegistry.getTargetContext().getApplicationContext();
|
||||
app.registerActivityLifecycleCallbacks(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||
mActivities.put(activity, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
}
|
||||
|
||||
public boolean noLeakedActivities() {
|
||||
int liveActivities = 0;
|
||||
int destroyedActivities = 0;
|
||||
|
||||
for (Activity activity : mActivities.keySet()) {
|
||||
if (activity.isDestroyed()) {
|
||||
++destroyedActivities;
|
||||
} else {
|
||||
++liveActivities;
|
||||
}
|
||||
}
|
||||
|
||||
// It's OK to have 1 leaked activity if no active activities exist.
|
||||
return liveActivities == 0 ? destroyedActivities <= 1 : destroyedActivities == 0;
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ class PortraitLandscapeRunner implements TestRule {
|
||||
private void evaluateInPortrait() throws Throwable {
|
||||
mTest.mDevice.setOrientationNatural();
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
|
||||
AbstractLauncherUiTest.checkDetectedLeaks();
|
||||
AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher);
|
||||
base.evaluate();
|
||||
mTest.getDevice().pressHome();
|
||||
}
|
||||
@@ -64,7 +64,7 @@ class PortraitLandscapeRunner implements TestRule {
|
||||
private void evaluateInLandscape() throws Throwable {
|
||||
mTest.mDevice.setOrientationLeft();
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
|
||||
AbstractLauncherUiTest.checkDetectedLeaks();
|
||||
AbstractLauncherUiTest.checkDetectedLeaks(mTest.mLauncher);
|
||||
base.evaluate();
|
||||
mTest.getDevice().pressHome();
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
test.waitForResumed("Launcher internal state is still Background");
|
||||
// Check that we switched to home.
|
||||
test.mLauncher.getWorkspace();
|
||||
AbstractLauncherUiTest.checkDetectedLeaks();
|
||||
AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher);
|
||||
}
|
||||
|
||||
// Please don't add negative test cases for methods that fail only after a long wait.
|
||||
|
||||
Reference in New Issue
Block a user