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

Commit d7aa79a9 authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka
Browse files

Per-test WindowManagerService instance

This CL starts using a WindowManagerService instance for each test method,
instead of sharing a single instance within a test class. It also starts
resetting mocks after each test method. With a MockTrack hack, it reduces
memory leaks happening in extended inline Mockito (b/123984854).

Bug: 123652272
Test: Pass all non-flaky presubmit tests in WmTests.
  $ tradefed.sh run commandAndExit WmTests \
      --include-annotation android.platform.test.annotations.Presubmit \
      --exclude-annotation androidx.test.filters.FlakyTest
Change-Id: Icbfd5c5d726e4c29a623682deac852c5b14667dd
parent c28fa880
Loading
Loading
Loading
Loading
+12 −14
Original line number Diff line number Diff line
@@ -74,7 +74,6 @@ import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.utils.MockTracker;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
@@ -89,8 +88,6 @@ import java.util.List;
class ActivityTestsBase {
    private static int sNextDisplayId = DEFAULT_DISPLAY + 1;

    private static MockTracker sMockTracker;

    @Rule
    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
            new DexmakerShareClassLoaderRule();
@@ -102,6 +99,8 @@ class ActivityTestsBase {
    RootActivityContainer mRootActivityContainer;
    ActivityStackSupervisor mSupervisor;

    private MockTracker mMockTracker;

    // Default package name
    static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";

@@ -110,19 +109,13 @@ class ActivityTestsBase {

    @BeforeClass
    public static void setUpOnceBase() {
        sMockTracker = new MockTracker();

        AttributeCache.init(getInstrumentation().getTargetContext());
    }

    @AfterClass
    public static void tearDownOnceBase() {
        sMockTracker.close();
        sMockTracker = null;
    }

    @Before
    public void setUpBase() {
        mMockTracker = new MockTracker();

        mTestInjector.setUp();

        mService = new TestActivityTaskManagerService(mContext);
@@ -137,6 +130,9 @@ class ActivityTestsBase {
            mService.setWindowManager(null);
            mService = null;
        }

        mMockTracker.close();
        mMockTracker = null;
    }

    /** Creates a {@link TestActivityDisplay}. */
@@ -651,7 +647,10 @@ class ActivityTestsBase {

        @Override
        protected DisplayContent createDisplayContent() {
            return WindowTestUtils.createTestDisplayContent();
            final DisplayContent displayContent = mock(DisplayContent.class);
            DockedStackDividerController divider = mock(DockedStackDividerController.class);
            doReturn(divider).when(displayContent).getDockedDividerController();
            return displayContent;
        }

        void removeAllTasks() {
@@ -732,14 +731,13 @@ class ActivityTestsBase {

        @Override
        protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
            mTaskStack = WindowTestUtils.createMockTaskStack();
            mTaskStack = mock(TaskStack.class);

            // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
            // will be moved to the full screen stack.
            if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                outBounds.set(0, 0, 100, 100);
            }

        }

        @Override
+6 −30
Original line number Diff line number Diff line
@@ -24,14 +24,13 @@ import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -39,9 +38,7 @@ import android.view.Display;

import androidx.test.filters.SmallTest;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
@@ -54,33 +51,12 @@ import org.junit.Test;
@Presubmit
public class AppTransitionTests extends WindowTestsBase {

    private static RootWindowContainer sOriginalRootWindowContainer;

    private DisplayContent mDc;

    @BeforeClass
    public static void setUpRootWindowContainerMock() {
        final WindowManagerService wm = TestSystemServices.getWindowManagerService();
        // For unit test, we don't need to test performSurfacePlacement to prevent some abnormal
        // interaction with surfaceflinger native side.
        sOriginalRootWindowContainer = wm.mRoot;
        // Creating spied mock of RootWindowContainer shouldn't be done in @Before, since it will
        // create unnecessary nested spied objects chain, because WindowManagerService object under
        // test is a single instance shared among all tests that extend WindowTestsBase class.
        // Instead it should be done once before running all tests in this test class.
        wm.mRoot = spy(wm.mRoot);
        doNothing().when(wm.mRoot).performSurfacePlacement(anyBoolean());
    }

    @AfterClass
    public static void tearDownRootWindowContainerMock() {
        final WindowManagerService wm = TestSystemServices.getWindowManagerService();
        wm.mRoot = sOriginalRootWindowContainer;
        sOriginalRootWindowContainer = null;
    }

    @Before
    public void setUp() throws Exception {
        spyOn(mWm.mRoot);
        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
        mDc = mWm.getDefaultDisplayContentLocked();
    }

+6 −7
Original line number Diff line number Diff line
@@ -26,11 +26,11 @@ import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.ContextWrapper;
@@ -65,8 +65,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
    DisplayPolicy mDisplayPolicy;

    @Before
    public void setUpBase() {
        super.setUpBase();
    public void setUpDisplayPolicy() {
        mDisplayPolicy = spy(mDisplayContent.getDisplayPolicy());

        final TestContextWrapper context =
@@ -77,9 +76,9 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
        resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
        resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
        resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
        when(mDisplayPolicy.getSystemUiContext()).thenReturn(context);
        when(mDisplayPolicy.hasNavigationBar()).thenReturn(true);
        when(mDisplayPolicy.hasStatusBar()).thenReturn(true);
        doReturn(context).when(mDisplayPolicy).getSystemUiContext();
        doReturn(true).when(mDisplayPolicy).hasNavigationBar();
        doReturn(true).when(mDisplayPolicy).hasStatusBar();

        final int shortSizeDp =
                Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
+1 −1
Original line number Diff line number Diff line
@@ -807,7 +807,7 @@ public class DisplayRotationTests {
        private void build() throws Exception {
            mMockContext = mock(Context.class);

            mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class);
            mMockDisplayContent = mock(DisplayContent.class);
            mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
            when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt()))
                    .thenReturn(WmDisplayCutout.NO_CUTOUT);
+80 −50
Original line number Diff line number Diff line
@@ -57,44 +57,61 @@ import com.android.server.LockGuard;
import com.android.server.Watchdog;
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.MockTracker;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.quality.Strictness;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * A Test utility class to create a mock {@link WindowManagerService} instance for tests.
 * JUnit test rule to create a mock {@link WindowManagerService} instance for tests.
 */
class TestSystemServices {
    private static StaticMockitoSession sMockitoSession;
    private static WindowManagerService sService;
    private static TestWindowManagerPolicy sPolicy;
public class SystemServicesTestRule implements TestRule {

    static AtomicBoolean sCurrentMessagesProcessed = new AtomicBoolean(false);
    private static final String TAG = SystemServicesTestRule.class.getSimpleName();

    static void setUpWindowManagerService() {
        sMockitoSession = mockitoSession()
                .spyStatic(LockGuard.class)
                .spyStatic(Watchdog.class)
                .strictness(Strictness.LENIENT)
                .startMocking();
    private final AtomicBoolean mCurrentMessagesProcessed = new AtomicBoolean(false);

        runWithDexmakerShareClassLoader(TestSystemServices::setUpTestWindowService);
    }
    private MockTracker mMockTracker;
    private StaticMockitoSession mMockitoSession;
    private WindowManagerService mWindowManagerService;
    private TestWindowManagerPolicy mWindowManagerPolicy;

    static void tearDownWindowManagerService() {
        waitUntilWindowManagerHandlersIdle();
        removeLocalServices();
        sService = null;
        sPolicy = null;
    /** {@link MockTracker} to track mocks created by {@link SystemServicesTestRule}. */
    private static class Tracker extends MockTracker {
        // This empty extended class is necessary since Mockito distinguishes a listener by it
        // class.
    }

        sMockitoSession.finishMocking();
        sMockitoSession = null;
    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                try {
                    runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp);
                    base.evaluate();
                } finally {
                    tearDown();
                }
            }
        };
    }

    private static void setUpTestWindowService() {
        doReturn(null).when(() -> LockGuard.installLock(any(), anyInt()));
    private void setUp() {
        mMockTracker = new Tracker();

        mMockitoSession = mockitoSession()
                .spyStatic(LocalServices.class)
                .mockStatic(LockGuard.class)
                .mockStatic(Watchdog.class)
                .strictness(Strictness.LENIENT)
                .startMocking();

        doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);

        final Context context = getInstrumentation().getTargetContext();
@@ -116,21 +133,18 @@ class TestSystemServices {
        doReturn(appOpsManager).when(context)
                .getSystemService(eq(Context.APP_OPS_SERVICE));

        removeLocalServices();

        final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
        LocalServices.addService(DisplayManagerInternal.class, dmi);
        doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class)));

        final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
        LocalServices.addService(PowerManagerInternal.class, pmi);
        final PowerSaveState state = new PowerSaveState.Builder().build();
        doReturn(state).when(pmi).getLowPowerState(anyInt());
        doReturn(pmi).when(() -> LocalServices.getService(eq(PowerManagerInternal.class)));

        final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
        LocalServices.addService(ActivityManagerInternal.class, ami);
        doReturn(ami).when(() -> LocalServices.getService(eq(ActivityManagerInternal.class)));

        final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
        LocalServices.addService(ActivityTaskManagerInternal.class, atmi);
        doAnswer((InvocationOnMock invocationOnMock) -> {
            final Runnable runnable = invocationOnMock.getArgument(0);
            if (runnable != null) {
@@ -138,6 +152,7 @@ class TestSystemServices {
            }
            return null;
        }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
        doReturn(atmi).when(() -> LocalServices.getService(eq(ActivityTaskManagerInternal.class)));

        final InputManagerService ims = mock(InputManagerService.class);
        // InputChannel is final and can't be mocked.
@@ -150,31 +165,46 @@ class TestSystemServices {
        final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
        doReturn(wmLock).when(atms).getGlobalLock();

        sPolicy = new TestWindowManagerPolicy(TestSystemServices::getWindowManagerService);
        sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms);
        mWindowManagerPolicy = new TestWindowManagerPolicy(this::getWindowManagerService);
        mWindowManagerService = WindowManagerService.main(
                context, ims, false, false, mWindowManagerPolicy, atms);

        sService.onInitReady();
        mWindowManagerService.onInitReady();

        final Display display = sService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
        final Display display = mWindowManagerService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
        // Display creation is driven by the ActivityManagerService via
        // ActivityStackSupervisor. We emulate those steps here.
        sService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
        mWindowManagerService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));

        mMockTracker.stopTracking();
    }

    private void tearDown() {
        waitUntilWindowManagerHandlersIdle();
        removeLocalServices();
        mWindowManagerService = null;
        mWindowManagerPolicy = null;
        if (mMockitoSession != null) {
            mMockitoSession.finishMocking();
            mMockitoSession = null;
        }

        if (mMockTracker != null) {
            mMockTracker.close();
            mMockTracker = null;
        }
    }

    private static void removeLocalServices() {
        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
        LocalServices.removeServiceForTest(PowerManagerInternal.class);
        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
        LocalServices.removeServiceForTest(WindowManagerInternal.class);
        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
    }

    static WindowManagerService getWindowManagerService() {
        return sService;
    WindowManagerService getWindowManagerService() {
        return mWindowManagerService;
    }

    static void cleanupWindowManagerHandlers() {
    void cleanupWindowManagerHandlers() {
        final WindowManagerService wm = getWindowManagerService();
        if (wm == null) {
            return;
@@ -184,7 +214,7 @@ class TestSystemServices {
        SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
    }

    static void waitUntilWindowManagerHandlersIdle() {
    void waitUntilWindowManagerHandlersIdle() {
        final WindowManagerService wm = getWindowManagerService();
        if (wm == null) {
            return;
@@ -196,21 +226,21 @@ class TestSystemServices {
        waitHandlerIdle(SurfaceAnimationThread.getHandler());
    }

    private static void waitHandlerIdle(Handler handler) {
        synchronized (sCurrentMessagesProcessed) {
    private void waitHandlerIdle(Handler handler) {
        synchronized (mCurrentMessagesProcessed) {
            // Add a message to the handler queue and make sure it is fully processed before we move
            // on. This makes sure all previous messages in the handler are fully processed vs. just
            // popping them from the message queue.
            sCurrentMessagesProcessed.set(false);
            mCurrentMessagesProcessed.set(false);
            handler.post(() -> {
                synchronized (sCurrentMessagesProcessed) {
                    sCurrentMessagesProcessed.set(true);
                    sCurrentMessagesProcessed.notifyAll();
                synchronized (mCurrentMessagesProcessed) {
                    mCurrentMessagesProcessed.set(true);
                    mCurrentMessagesProcessed.notifyAll();
                }
            });
            while (!sCurrentMessagesProcessed.get()) {
            while (!mCurrentMessagesProcessed.get()) {
                try {
                    sCurrentMessagesProcessed.wait();
                    mCurrentMessagesProcessed.wait();
                } catch (InterruptedException e) {
                }
            }
Loading