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

Commit bb52561f authored by Bryce Lee's avatar Bryce Lee
Browse files

Convert DreamOverlayServiceTest to Kotlin.

This changelist refactors DreamoverlayServiceTest to Kotlin to make
future testing easier.

Test: atest DreamOverlayServiceTest
Bug: 334083490
Flag: N/A
Change-Id: Ic31d5bce52b8a99f570924da2d0c3bfe6e709852
parent 0a1e44ad
Loading
Loading
Loading
Loading
+0 −559
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.systemui.dreams;

import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;

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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.dreams.IDreamOverlay;
import android.service.dreams.IDreamOverlayCallback;
import android.service.dreams.IDreamOverlayClient;
import android.service.dreams.IDreamOverlayClientCallback;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManagerImpl;

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.ambient.touch.TouchMonitor;
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
import com.android.systemui.complication.ComplicationLayoutEngine;
import com.android.systemui.dreams.complication.HideComplicationTouchHandler;
import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.LeakCheckedTest;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamOverlayServiceTest extends SysuiTestCase {
    private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package",
            "lowlight");

    private static final ComponentName HOME_CONTROL_PANEL_DREAM_COMPONENT =
            new ComponentName("package", "homeControlPanel");
    private static final String DREAM_COMPONENT = "package/dream";
    private static final String WINDOW_NAME = "test";
    private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
    private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);

    @Mock
    DreamOverlayLifecycleOwner mLifecycleOwner;

    @Mock
    LifecycleRegistry mLifecycleRegistry;

    @Rule
    public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();

    WindowManager.LayoutParams mWindowParams;

    @Mock
    IDreamOverlayCallback mDreamOverlayCallback;

    @Mock
    WindowManagerImpl mWindowManager;

    @Mock
    com.android.systemui.complication.dagger.ComplicationComponent.Factory
            mComplicationComponentFactory;

    @Mock
    com.android.systemui.complication.dagger.ComplicationComponent mComplicationComponent;

    @Mock
    ComplicationLayoutEngine mComplicationVisibilityController;

    @Mock
    ComplicationComponent.Factory mDreamComplicationComponentFactory;

    @Mock
    ComplicationComponent mDreamComplicationComponent;

    @Mock
    HideComplicationTouchHandler mHideComplicationTouchHandler;

    @Mock
    DreamOverlayComponent.Factory mDreamOverlayComponentFactory;

    @Mock
    DreamOverlayComponent mDreamOverlayComponent;

    @Mock
    AmbientTouchComponent.Factory mAmbientTouchComponentFactory;

    @Mock
    AmbientTouchComponent mAmbientTouchComponent;

    @Mock
    DreamOverlayContainerView mDreamOverlayContainerView;

    @Mock
    DreamOverlayContainerViewController mDreamOverlayContainerViewController;

    @Mock
    KeyguardUpdateMonitor mKeyguardUpdateMonitor;

    @Mock
    TouchMonitor mTouchMonitor;

    @Mock
    DreamOverlayStateController mStateController;

    @Mock
    ViewGroup mDreamOverlayContainerViewParent;

    @Mock
    TouchInsetManager mTouchInsetManager;

    @Mock
    UiEventLogger mUiEventLogger;

    @Mock
    DreamOverlayCallbackController mDreamOverlayCallbackController;

    @Captor
    ArgumentCaptor<View> mViewCaptor;

    DreamOverlayService mService;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);

        when(mDreamOverlayComponent.getDreamOverlayContainerViewController())
                .thenReturn(mDreamOverlayContainerViewController);
        when(mLifecycleOwner.getRegistry())
                .thenReturn(mLifecycleRegistry);
        when(mComplicationComponentFactory
                .create(any(), any(), any(), any()))
                .thenReturn(mComplicationComponent);
        when(mComplicationComponent.getVisibilityController())
                .thenReturn(mComplicationVisibilityController);
        when(mDreamComplicationComponent.getHideComplicationTouchHandler())
                .thenReturn(mHideComplicationTouchHandler);
        when(mDreamComplicationComponentFactory
                .create(any(), any()))
                .thenReturn(mDreamComplicationComponent);
        when(mDreamOverlayComponentFactory
                .create(any(), any(), any()))
                .thenReturn(mDreamOverlayComponent);
        when(mAmbientTouchComponentFactory.create(any(), any())).thenReturn(mAmbientTouchComponent);
        when(mAmbientTouchComponent.getTouchMonitor())
                .thenReturn(mTouchMonitor);
        when(mDreamOverlayContainerViewController.getContainerView())
                .thenReturn(mDreamOverlayContainerView);

        mWindowParams = new WindowManager.LayoutParams();
        mService = new DreamOverlayService(
                mContext,
                mLifecycleOwner,
                mMainExecutor,
                mWindowManager,
                mComplicationComponentFactory,
                mDreamComplicationComponentFactory,
                mDreamOverlayComponentFactory,
                mAmbientTouchComponentFactory,
                mStateController,
                mKeyguardUpdateMonitor,
                mUiEventLogger,
                mTouchInsetManager,
                LOW_LIGHT_COMPONENT,
                HOME_CONTROL_PANEL_DREAM_COMPONENT,
                mDreamOverlayCallbackController,
                WINDOW_NAME);
    }

    public IDreamOverlayClient getClient() throws RemoteException {
        final IBinder proxy = mService.onBind(new Intent());
        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
        final IDreamOverlayClientCallback callback =
                Mockito.mock(IDreamOverlayClientCallback.class);
        overlay.getClient(callback);
        final ArgumentCaptor<IDreamOverlayClient> clientCaptor =
                ArgumentCaptor.forClass(IDreamOverlayClient.class);
        verify(callback).onDreamOverlayClient(clientCaptor.capture());

        return clientCaptor.getValue();
    }

    @Test
    public void testOnStartMetricsLogged() throws Exception {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
        verify(mUiEventLogger).log(
                DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
    }

    @Test
    public void testOverlayContainerViewAddedToWindow() throws Exception {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        verify(mWindowManager).addView(any(), any());
    }

    // Validates that {@link DreamOverlayService} properly handles the case where the dream's
    // window is no longer valid by the time start is called.
    @Test
    public void testInvalidWindowAddStart() throws Exception {
        final IDreamOverlayClient client = getClient();

        doThrow(new WindowManager.BadTokenException()).when(mWindowManager).addView(any(), any());
        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        verify(mWindowManager).addView(any(), any());

        verify(mStateController).setOverlayActive(false);
        verify(mStateController).setLowLightActive(false);
        verify(mStateController).setEntryAnimationsFinished(false);

        verify(mStateController, never()).setOverlayActive(true);
        verify(mUiEventLogger, never()).log(
                DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);

        verify(mDreamOverlayCallbackController, never()).onStartDream();
    }

    @Test
    public void testDreamOverlayContainerViewControllerInitialized() throws Exception {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        verify(mDreamOverlayContainerViewController).init();
    }

    @Test
    public void testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized()
            throws Exception {
        when(mDreamOverlayContainerView.getParent())
                .thenReturn(mDreamOverlayContainerViewParent)
                .thenReturn(null);

        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView);
    }

    @Test
    public void testShouldShowComplicationsSetByStartDream() throws RemoteException {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                true /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        assertThat(mService.shouldShowComplications()).isTrue();
    }

    @Test
    public void testLowLightSetByStartDream() throws RemoteException {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback,
                LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT);
        verify(mStateController).setLowLightActive(true);
    }

    @Test
    public void testHomeControlPanelSetsByStartDream() throws RemoteException {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback,
                HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(),
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();
        assertThat(mService.getDreamComponent()).isEqualTo(HOME_CONTROL_PANEL_DREAM_COMPONENT);
        verify(mStateController).setHomeControlPanelActive(true);
    }

    @Test
    public void testOnEndDream() throws RemoteException {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback,
                LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        // Verify view added.
        verify(mWindowManager).addView(mViewCaptor.capture(), any());

        // Service destroyed.
        mService.onEndDream();
        mMainExecutor.runAllReady();

        // Verify view removed.
        verify(mWindowManager).removeView(mViewCaptor.getValue());

        // Verify state correctly set.
        verify(mStateController).setOverlayActive(false);
        verify(mStateController).setLowLightActive(false);
        verify(mStateController).setEntryAnimationsFinished(false);
    }

    @Test
    public void testImmediateEndDream() throws Exception {
        final IDreamOverlayClient client = getClient();

        // Start the dream, but don't execute any Runnables put on the executor yet. We delay
        // executing Runnables as the timing isn't guaranteed and we want to verify that the overlay
        // starts and finishes in the proper order even if Runnables are delayed.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        // Immediately end the dream.
        client.endDream();
        // Run any scheduled Runnables.
        mMainExecutor.runAllReady();

        // The overlay starts then finishes.
        InOrder inOrder = inOrder(mWindowManager);
        inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any());
        inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue());
    }

    @Test
    public void testEndDreamDuringStartDream() throws Exception {
        final IDreamOverlayClient client = getClient();

        // Schedule the endDream call in the middle of the startDream implementation, as any
        // ordering is possible.
        doAnswer(invocation -> {
            client.endDream();
            return null;
        }).when(mStateController).setOverlayActive(true);

        // Start the dream.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        // The overlay starts then finishes.
        InOrder inOrder = inOrder(mWindowManager);
        inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any());
        inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue());
    }

    @Test
    public void testDestroy() throws RemoteException {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback,
                LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        // Verify view added.
        verify(mWindowManager).addView(mViewCaptor.capture(), any());

        // Service destroyed.
        mService.onDestroy();
        mMainExecutor.runAllReady();

        // Verify view removed.
        verify(mWindowManager).removeView(mViewCaptor.getValue());

        // Verify state correctly set.
        verify(mKeyguardUpdateMonitor).removeCallback(any());
        verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
        verify(mStateController).setOverlayActive(false);
        verify(mStateController).setLowLightActive(false);
        verify(mStateController).setEntryAnimationsFinished(false);
    }

    @Test
    public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() {
        // Service destroyed without ever starting dream.
        mService.onDestroy();
        mMainExecutor.runAllReady();

        // Verify no view is removed.
        verify(mWindowManager, never()).removeView(any());

        // Verify state still correctly set.
        verify(mKeyguardUpdateMonitor).removeCallback(any());
        verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
        verify(mStateController).setOverlayActive(false);
        verify(mStateController).setLowLightActive(false);
    }

    @Test
    public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception {
        final IDreamOverlayClient client = getClient();

        // Destroy the service.
        mService.onDestroy();
        mMainExecutor.runAllReady();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        verify(mWindowManager, never()).addView(any(), any());
    }

    @Test
    public void testNeverRemoveDecorViewIfNotAdded() {
        // Service destroyed before dream started.
        mService.onDestroy();
        mMainExecutor.runAllReady();

        verify(mWindowManager, never()).removeView(any());
    }

    @Test
    public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting. Do not show dream complications.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        // Verify that a new window is added.
        verify(mWindowManager).addView(mViewCaptor.capture(), any());
        final View windowDecorView = mViewCaptor.getValue();

        // Assert that the overlay is not showing complications.
        assertThat(mService.shouldShowComplications()).isFalse();

        clearInvocations(mDreamOverlayComponent);
        clearInvocations(mAmbientTouchComponent);
        clearInvocations(mWindowManager);

        // New dream starting with dream complications showing. Note that when a new dream is
        // binding to the dream overlay service, it receives the same instance of IBinder as the
        // first one.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                true /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        // Assert that the overlay is showing complications.
        assertThat(mService.shouldShowComplications()).isTrue();

        // Verify that the old overlay window has been removed, and a new one created.
        verify(mWindowManager).removeView(windowDecorView);
        verify(mWindowManager).addView(any(), any());

        // Verify that new instances of overlay container view controller and overlay touch monitor
        // are created.
        verify(mDreamOverlayComponent).getDreamOverlayContainerViewController();
        verify(mAmbientTouchComponent).getTouchMonitor();
    }

    @Test
    public void testWakeUp() throws RemoteException {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                true /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        mService.onWakeUp();
        verify(mDreamOverlayContainerViewController).wakeUp();
        verify(mDreamOverlayCallbackController).onWakeUp();
    }

    @Test
    public void testWakeUpBeforeStartDoesNothing() {
        mService.onWakeUp();
        verify(mDreamOverlayContainerViewController, never()).wakeUp();
    }

    @Test
    public void testSystemFlagShowForAllUsersSetOnWindow() throws RemoteException {
        final IDreamOverlayClient client = getClient();

        // Inform the overlay service of dream starting. Do not show dream complications.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        final ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor =
                ArgumentCaptor.forClass(WindowManager.LayoutParams.class);

        // Verify that a new window is added.
        verify(mWindowManager).addView(any(), paramsCaptor.capture());

        assertThat((paramsCaptor.getValue().privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
                == SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isTrue();
    }
}
+573 −0

File added.

Preview size limit exceeded, changes collapsed.