From 5ca09e9d0dcba2be2afd9de561fdb858df7e788d Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Fri, 8 Feb 2019 17:25:01 -0800 Subject: Clear spiedInstance field for spyOn objects. All spyOn instances are directly returned as the mock, and put in the mock map with a strong ref in the value of the map. Due to the strong ref in the value the mock map won't purge that item. Note this is not a complete solution, for the same reason as its parent commit. MockCreationListner is registered to a ThreadLocal, so we can only track mocks created in the same thread where it's registered. That includes all befores and afters, but we basically lose all mocks created in test cases because they're run with timeout in a different thread. They are mostly ActivityRecords. Bug: 123984854 Test: Smaller memory pressure shown in mem dump. Change-Id: If3c488a23ab9c59a63d9844fc995e4bb0313896a --- .../com/android/server/wm/utils/MockTracker.java | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java index ef955fb10264..1ce463bbbd9e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java @@ -20,9 +20,11 @@ import android.util.Log; import org.mockito.Mockito; import org.mockito.MockitoFramework; +import org.mockito.internal.creation.settings.CreationSettings; import org.mockito.listeners.MockCreationListener; import org.mockito.mock.MockCreationSettings; +import java.lang.reflect.Field; import java.util.IdentityHashMap; /** @@ -33,6 +35,17 @@ import java.util.IdentityHashMap; public class MockTracker implements MockCreationListener, AutoCloseable { private static final String TAG = "MockTracker"; + private static final Field SPIED_INSTANCE_FIELD; + + static { + try { + SPIED_INSTANCE_FIELD = CreationSettings.class.getDeclaredField("spiedInstance"); + SPIED_INSTANCE_FIELD.setAccessible(true); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + private final MockitoFramework mMockitoFramework = Mockito.framework(); private final IdentityHashMap mMocks = new IdentityHashMap<>(); @@ -44,6 +57,25 @@ public class MockTracker implements MockCreationListener, AutoCloseable { @Override public void onMockCreated(Object mock, MockCreationSettings settings) { mMocks.put(mock, null); + clearSpiedInstanceIfNeeded(mock, settings); + } + + // HACK: Changing Mockito core implementation details. + // TODO(b/123984854): Remove this once there is a real fix. + private void clearSpiedInstanceIfNeeded(Object mock, MockCreationSettings settings) { + if (mock != settings.getSpiedInstance()) { + // Not a spyOn instance. + return; + } + if (!(settings instanceof CreationSettings)) { + throw new IllegalStateException("Unexpected type of settings: " + settings.getClass()); + } + try { + SPIED_INSTANCE_FIELD.set(settings, null); + Log.d(TAG, "Setting spiedInstance for " + mock + " to null."); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } } @Override -- cgit v1.2.3-59-g8ed1b