Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5ca09e9d authored by Garfield Tan's avatar Garfield Tan
Browse files

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
parent 26835f0b
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -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<Object, Void> 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