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

Commit 55544c88 authored by Garfield Tan's avatar Garfield Tan
Browse files

Add unit tests for layout of window decoration

This also fixes a few bugs discovered by this unit test.

Bug: 240202224
Test: atest WindowDecorationTest with caption on shell enabled.
Change-Id: I60f75b460e2349666acbd839ba8f81fc9c96d7b6
parent 7560f94e
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -222,10 +222,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>

        float shadowRadius = outResult.mDensity * shadowRadiusDp;
        int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
        mTmpColor[0] = Color.red(backgroundColorInt);
        mTmpColor[1] = Color.green(backgroundColorInt);
        mTmpColor[2] = Color.blue(backgroundColorInt);
        t.setCrop(mTaskBackgroundSurface, taskBounds)
        mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
        mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
        mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
        t.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(), taskBounds.height())
                .setShadowRadius(mTaskBackgroundSurface, shadowRadius)
                .setColor(mTaskBackgroundSurface, mTmpColor);

+2 −3
Original line number Diff line number Diff line
@@ -34,13 +34,12 @@ public class MockSurfaceControlHelper {
     * given {@link SurfaceControl} when calling {@link SurfaceControl.Builder#build()}.
     *
     * @param mockSurfaceControl the first {@link SurfaceControl} to return
     * @param mockSurfaceControls following {@link SurfaceControl} to return
     * @return the mock of {@link SurfaceControl.Builder}
     */
    public static SurfaceControl.Builder createMockSurfaceControlBuilder(
            SurfaceControl mockSurfaceControl, SurfaceControl... mockSurfaceControls) {
            SurfaceControl mockSurfaceControl) {
        final SurfaceControl.Builder mockBuilder = mock(SurfaceControl.Builder.class, RETURNS_SELF);
        doReturn(mockSurfaceControl, (Object[]) mockSurfaceControls)
        doReturn(mockSurfaceControl)
                .when(mockBuilder)
                .build();
        return mockBuilder;
+151 −3
Original line number Diff line number Diff line
@@ -21,23 +21,32 @@ import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceCon

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;

import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowManager.LayoutParams;
import android.window.WindowContainerTransaction;

import androidx.test.filters.SmallTest;
@@ -53,6 +62,8 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/**
@@ -66,6 +77,8 @@ import java.util.function.Supplier;
public class WindowDecorationTests extends ShellTestCase {
    private static final int CAPTION_HEIGHT_DP = 32;
    private static final int SHADOW_RADIUS_DP = 5;
    private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
    private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);

    private final Rect mOutsetsDp = new Rect();
    private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
@@ -84,18 +97,139 @@ public class WindowDecorationTests extends ShellTestCase {
    @Mock
    private WindowContainerTransaction mMockWindowContainerTransaction;

    private SurfaceControl.Builder mMockSurfaceControlBuilder;
    private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>();
    private SurfaceControl.Transaction mMockSurfaceControlTransaction;

    @Before
    public void setUp() {
        mMockSurfaceControlBuilder = createMockSurfaceControlBuilder(mock(SurfaceControl.class));
        mMockSurfaceControlTransaction = createMockSurfaceControlTransaction();

        doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
                .create(any(), any(), any(), anyBoolean());
    }

    @Test
    public void testLayoutResultCalculation_invisibleTask() {
        final Display defaultDisplay = mock(Display.class);
        doReturn(defaultDisplay).when(mMockDisplayController)
                .getDisplay(Display.DEFAULT_DISPLAY);

        final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
        final SurfaceControl.Builder decorContainerSurfaceBuilder =
                createMockSurfaceControlBuilder(decorContainerSurface);
        mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
        final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
        final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
                createMockSurfaceControlBuilder(taskBackgroundSurface);
        mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);

        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
                new ActivityManager.TaskDescription.Builder()
                        .setBackgroundColor(Color.YELLOW);
        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
                .setDisplayId(Display.DEFAULT_DISPLAY)
                .setTaskDescriptionBuilder(taskDescriptionBuilder)
                .setBounds(TASK_BOUNDS)
                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
                .setVisible(false)
                .build();
        taskInfo.isFocused = false;
        // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
        // 64px.
        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
        mOutsetsDp.set(10, 20, 30, 40);

        final SurfaceControl taskSurface = mock(SurfaceControl.class);
        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);

        windowDecor.relayout(taskInfo);

        verify(decorContainerSurfaceBuilder, never()).build();
        verify(taskBackgroundSurfaceBuilder, never()).build();
        verify(mMockSurfaceControlViewHostFactory, never())
                .create(any(), any(), any(), anyBoolean());

        verify(mMockSurfaceControlTransaction).hide(taskSurface);

        assertNull(mRelayoutResult.mRootView);
    }

    @Test
    public void testLayoutResultCalculation_visibleFocusedTask() {
        final Display defaultDisplay = mock(Display.class);
        doReturn(defaultDisplay).when(mMockDisplayController)
                .getDisplay(Display.DEFAULT_DISPLAY);

        final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
        final SurfaceControl.Builder decorContainerSurfaceBuilder =
                createMockSurfaceControlBuilder(decorContainerSurface);
        mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
        final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
        final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
                createMockSurfaceControlBuilder(taskBackgroundSurface);
        mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);

        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
                new ActivityManager.TaskDescription.Builder()
                        .setBackgroundColor(Color.YELLOW);
        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
                .setDisplayId(Display.DEFAULT_DISPLAY)
                .setTaskDescriptionBuilder(taskDescriptionBuilder)
                .setBounds(TASK_BOUNDS)
                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
                .setVisible(true)
                .build();
        taskInfo.isFocused = true;
        // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
        // 64px.
        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
        mOutsetsDp.set(10, 20, 30, 40);

        final SurfaceControl taskSurface = mock(SurfaceControl.class);
        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);

        windowDecor.relayout(taskInfo);

        verify(decorContainerSurfaceBuilder).setParent(taskSurface);
        verify(decorContainerSurfaceBuilder).setContainerLayer();
        verify(mMockSurfaceControlTransaction).setTrustedOverlay(decorContainerSurface, true);
        verify(mMockSurfaceControlTransaction).setPosition(decorContainerSurface, -20, -40);
        verify(mMockSurfaceControlTransaction).setWindowCrop(decorContainerSurface, 380, 220);

        verify(taskBackgroundSurfaceBuilder).setParent(taskSurface);
        verify(taskBackgroundSurfaceBuilder).setEffectLayer();
        verify(mMockSurfaceControlTransaction).setWindowCrop(taskBackgroundSurface, 300, 100);
        verify(mMockSurfaceControlTransaction)
                .setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
        verify(mMockSurfaceControlTransaction).setShadowRadius(taskBackgroundSurface, 10);

        verify(mMockSurfaceControlViewHostFactory)
                .create(any(), eq(defaultDisplay), any(), anyBoolean());
        verify(mMockSurfaceControlViewHost)
                .setView(same(mMockView),
                        argThat(lp -> lp.height == 64
                                && lp.width == 300
                                && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
        if (ViewRootImpl.CAPTION_ON_SHELL) {
            verify(mMockView).setTaskFocusState(true);
            verify(mMockWindowContainerTransaction)
                    .addRectInsetsProvider(taskInfo.token,
                            new Rect(100, 300, 400, 364),
                            new int[] { InsetsState.ITYPE_CAPTION_BAR });
        }

        verify(mMockSurfaceControlTransaction)
                .setPosition(taskSurface, TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y);
        verify(mMockSurfaceControlTransaction)
                .setCrop(taskSurface, new Rect(-20, -40, 360, 180));
        verify(mMockSurfaceControlTransaction)
                .show(taskSurface);

        assertEquals(380, mRelayoutResult.mWidth);
        assertEquals(220, mRelayoutResult.mHeight);
        assertEquals(2, mRelayoutResult.mDensity, 0.f);
    }

    @Test
    public void testNotCrashWhenDisplayAppearsAfterTask() {
        doReturn(mock(Display.class)).when(mMockDisplayController)
@@ -145,10 +279,24 @@ public class WindowDecorationTests extends ShellTestCase {
    private TestWindowDecoration createWindowDecoration(
            ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
        return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
                taskInfo, testSurface, () -> mMockSurfaceControlBuilder,
                taskInfo, testSurface, new MockSurfaceControlBuilderSupplier(),
                mMockSurfaceControlViewHostFactory);
    }

    private class MockSurfaceControlBuilderSupplier implements Supplier<SurfaceControl.Builder> {
        private int mNumOfCalls = 0;

        @Override
        public SurfaceControl.Builder get() {
            final SurfaceControl.Builder builder =
                    mNumOfCalls < mMockSurfaceControlBuilders.size()
                            ? mMockSurfaceControlBuilders.get(mNumOfCalls)
                            : createMockSurfaceControlBuilder(mock(SurfaceControl.class));
            ++mNumOfCalls;
            return builder;
        }
    }

    private static class TestView extends View implements TaskFocusStateConsumer {
        private TestView(Context context) {
            super(context);