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

Commit fedfbed5 authored by Ale Nijamkin's avatar Ale Nijamkin
Browse files

[flexiglass] Fixes SysUiState related tests

SysUiState is a pub-sub-hub that preceeds Flexiglass. Multiple codepaths
write to and read state from it. Early in the Flexiglass project, a
SceneContainerPlugin was created that would act as middleman between
writers of state and SysUiState, replacing values written by legacy code
with Flexiglass-fitting ones (for example, whether the bouncer is
showing comes from the SceneInteractor in Flexiglass instead of the
keyguard code in legacy).

This plugin messes with this test. The approach I've taken is to provide
a fake plugin that defaults to overridding nothing.

Bug: 283121968
Test: N/A test only
Flag: EXEMPT test only
Change-Id: Iab6c4e3f81d1e0c0c380b815fade73d3e5bbbdc8
parent 3a5aaa90
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ class SceneContainerPluginTest : SysuiTestCase() {
    private val shadeDisplayRepository = kosmos.fakeShadeDisplaysRepository
    private val sceneDataSource = kosmos.fakeSceneDataSource

    private val underTest = kosmos.sceneContainerPlugin
    private val underTest: SceneContainerPlugin = kosmos.sceneContainerPluginImpl

    @Test
    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+0 −165
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.model;


import static android.view.Display.DEFAULT_DISPLAY;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.view.Display;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.kosmos.KosmosJavaAdapter;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class SysUiStateTest extends SysuiTestCase {
    private static final int FLAG_1 = 1;
    private static final int FLAG_2 = 1 << 1;
    private static final int FLAG_3 = 1 << 2;
    private static final int FLAG_4 = 1 << 3;
    private static final int DISPLAY_ID = DEFAULT_DISPLAY;

    private KosmosJavaAdapter mKosmos;
    private SysUiState.SysUiStateCallback mCallback;
    private SysUiState mFlagsContainer;
    private SceneContainerPlugin mSceneContainerPlugin;
    private DumpManager mDumpManager;
    private SysUIStateDispatcher mSysUIStateDispatcher;

    private SysUiState createInstance(int displayId) {
        var sysuiState = new SysUiStateImpl(displayId, mSceneContainerPlugin, mDumpManager,
                mSysUIStateDispatcher);
        sysuiState.addCallback(mCallback);
        return sysuiState;
    }

    @Before
    public void setup() {
        mKosmos = new KosmosJavaAdapter(this);
        mFlagsContainer = mKosmos.getSysuiState();
        mSceneContainerPlugin = mKosmos.getSceneContainerPlugin();
        mCallback = mock(SysUiState.SysUiStateCallback.class);
        mDumpManager = mock(DumpManager.class);
        mSysUIStateDispatcher = mKosmos.getSysUIStateDispatcher();
        mFlagsContainer = createInstance(DEFAULT_DISPLAY);
    }

    @Test
    public void addSingle_setFlag() {
        setFlags(FLAG_1);

        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
    }

    @Test
    public void addMultiple_setFlag() {
        setFlags(FLAG_1);
        setFlags(FLAG_2);

        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1 | FLAG_2, DEFAULT_DISPLAY);
    }

    @Test
    public void addMultipleRemoveOne_setFlag() {
        setFlags(FLAG_1);
        setFlags(FLAG_2);
        mFlagsContainer.setFlag(FLAG_1, false).commitUpdate(DISPLAY_ID);

        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1 | FLAG_2, DEFAULT_DISPLAY);
        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_2, DEFAULT_DISPLAY);
    }

    @Test
    public void addMultiple_setFlags() {
        setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4);

        int expected = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
        verify(mCallback, times(1)).onSystemUiStateChanged(expected, DEFAULT_DISPLAY);
    }

    @Test
    public void addMultipleRemoveOne_setFlags() {
        setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4);
        mFlagsContainer.setFlag(FLAG_2, false).commitUpdate(DISPLAY_ID);

        int expected1 = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
        verify(mCallback, times(1)).onSystemUiStateChanged(expected1, DEFAULT_DISPLAY);
        int expected2 = FLAG_1 | FLAG_3 | FLAG_4;
        verify(mCallback, times(1)).onSystemUiStateChanged(expected2, DEFAULT_DISPLAY);
    }

    @Test
    public void removeCallback() {
        mFlagsContainer.removeCallback(mCallback);
        setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4);

        int expected = FLAG_1 | FLAG_2 | FLAG_3 | FLAG_4;
        verify(mCallback, times(0)).onSystemUiStateChanged(expected, DEFAULT_DISPLAY);
    }

    @Test
    public void setFlag_receivedForDefaultDisplay() {
        setFlags(FLAG_1);

        verify(mCallback, times(1)).onSystemUiStateChanged(FLAG_1, DEFAULT_DISPLAY);
    }


    @Test
    public void init_registersWithDumpManager() {
        mFlagsContainer.start();

        verify(mDumpManager).registerNormalDumpable(any(), eq(mFlagsContainer));
    }

    @Test
    public void destroy_unregistersWithDumpManager() {
        mFlagsContainer.destroy();

        verify(mDumpManager).unregisterDumpable(anyString());
    }

    private void setFlags(int... flags) {
        setFlags(mFlagsContainer, flags);
    }

    private void setFlags(SysUiState instance, int... flags) {
        for (int flag : flags) {
            instance.setFlag(flag, true);
        }
        instance.commitUpdate();
    }
}
+165 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.model

import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.runTest
import com.android.systemui.model.SysUiState.SysUiStateCallback
import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
open class SysUiStateTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val callback: SysUiStateCallback = mock()

    private lateinit var underTest: SysUiState

    @Before
    fun setup() {
        underTest = createInstance(Display.DEFAULT_DISPLAY)
    }

    @Test
    fun addSingle_setFlag() =
        kosmos.runTest {
            setFlags(FLAG_1)

            verify(callback, times(1)).onSystemUiStateChanged(FLAG_1, Display.DEFAULT_DISPLAY)
        }

    @Test
    fun addMultiple_setFlag() =
        kosmos.runTest {
            setFlags(FLAG_1)
            setFlags(FLAG_2)

            verify(callback, times(1)).onSystemUiStateChanged(FLAG_1, Display.DEFAULT_DISPLAY)
            verify(callback, times(1))
                .onSystemUiStateChanged((FLAG_1 or FLAG_2), Display.DEFAULT_DISPLAY)
        }

    @Test
    fun addMultipleRemoveOne_setFlag() =
        kosmos.runTest {
            setFlags(FLAG_1)
            setFlags(FLAG_2)
            underTest.setFlag(FLAG_1, false).commitUpdate(DISPLAY_ID)

            verify(callback, times(1)).onSystemUiStateChanged(FLAG_1, Display.DEFAULT_DISPLAY)
            verify(callback, times(1))
                .onSystemUiStateChanged((FLAG_1 or FLAG_2), Display.DEFAULT_DISPLAY)
            verify(callback, times(1)).onSystemUiStateChanged(FLAG_2, Display.DEFAULT_DISPLAY)
        }

    @Test
    fun addMultiple_setFlags() =
        kosmos.runTest {
            setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4)

            val expected = FLAG_1 or FLAG_2 or FLAG_3 or FLAG_4
            verify(callback, times(1)).onSystemUiStateChanged(expected, Display.DEFAULT_DISPLAY)
        }

    @Test
    fun addMultipleRemoveOne_setFlags() =
        kosmos.runTest {
            setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4)
            underTest.setFlag(FLAG_2, false).commitUpdate(DISPLAY_ID)

            val expected1 = FLAG_1 or FLAG_2 or FLAG_3 or FLAG_4
            verify(callback, times(1)).onSystemUiStateChanged(expected1, Display.DEFAULT_DISPLAY)
            val expected2 = FLAG_1 or FLAG_3 or FLAG_4
            verify(callback, times(1)).onSystemUiStateChanged(expected2, Display.DEFAULT_DISPLAY)
        }

    @Test
    fun removeCallback() =
        kosmos.runTest {
            underTest.removeCallback(callback)
            setFlags(FLAG_1, FLAG_2, FLAG_3, FLAG_4)

            val expected = FLAG_1 or FLAG_2 or FLAG_3 or FLAG_4
            verify(callback, times(0)).onSystemUiStateChanged(expected, Display.DEFAULT_DISPLAY)
        }

    @Test
    fun setFlag_receivedForDefaultDisplay() =
        kosmos.runTest {
            setFlags(FLAG_1)

            verify(callback, times(1)).onSystemUiStateChanged(FLAG_1, Display.DEFAULT_DISPLAY)
        }

    @Test
    fun init_registersWithDumpManager() =
        kosmos.runTest {
            underTest.start()

            verify(dumpManager).registerNormalDumpable(any(), eq(underTest))
        }

    @Test
    fun destroy_unregistersWithDumpManager() =
        kosmos.runTest {
            underTest.destroy()

            verify(dumpManager).unregisterDumpable(anyString())
        }

    private fun createInstance(displayId: Int): SysUiState {
        return SysUiStateImpl(
                displayId,
                kosmos.fakeSceneContainerPlugin,
                kosmos.dumpManager,
                kosmos.sysUIStateDispatcher,
            )
            .apply { addCallback(callback) }
    }

    private fun setFlags(vararg flags: Long) {
        setFlags(underTest, *flags)
    }

    private fun setFlags(instance: SysUiState, vararg flags: Long) {
        for (flag in flags) {
            instance.setFlag(flag, true)
        }
        instance.commitUpdate()
    }

    companion object {
        private const val FLAG_1 = 1L
        private const val FLAG_2 = 1L shl 1
        private const val FLAG_3 = 1L shl 2
        private const val FLAG_4 = 1L shl 3
        private const val DISPLAY_ID = Display.DEFAULT_DISPLAY
    }
}
+20 −11
Original line number Diff line number Diff line
@@ -21,9 +21,12 @@ import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.display.data.repository.DisplayComponentRepository
import com.android.systemui.display.data.repository.PerDisplayCoroutineScopeRepositoryModule
import com.android.systemui.model.SceneContainerPlugin
import com.android.systemui.model.SceneContainerPluginImpl
import com.android.systemui.model.SysUIStateInstanceProvider
import com.android.systemui.model.SysUiState
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import dagger.Binds
import dagger.Module
import dagger.Provides

@@ -31,8 +34,13 @@ import dagger.Provides
@Module(
    includes = [PerDisplayCoroutineScopeRepositoryModule::class, DisplayComponentRepository::class]
)
class PerDisplayRepositoriesModule {
interface PerDisplayRepositoriesModule {

    @Binds
    @SysUISingleton
    fun bindSceneContainerPlugin(impl: SceneContainerPluginImpl): SceneContainerPlugin

    companion object {
        @SysUISingleton
        @Provides
        fun provideSysUiStateRepository(
@@ -47,3 +55,4 @@ class PerDisplayRepositoriesModule {
            }
        }
    }
}
+20 −16
Original line number Diff line number Diff line
@@ -43,22 +43,33 @@ import kotlinx.coroutines.flow.StateFlow
 * A plugin for [SysUiState] that provides overrides for certain state flags that must be pulled
 * from the scene framework when that framework is enabled.
 */
interface SceneContainerPlugin {
    /**
     * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled
     * or if the flag value doesn't need to be overridden.
     */
    fun flagValueOverride(@SystemUiStateFlags flag: Long, displayId: Int): Boolean?

    data class SceneContainerPluginState(
        val scene: SceneKey,
        val overlays: Set<OverlayKey>,
        val invisibleDueToOcclusion: Boolean,
        val isVisible: Boolean,
    )
}

@SysUISingleton
class SceneContainerPlugin
class SceneContainerPluginImpl
@Inject
constructor(
    private val sceneInteractor: Lazy<SceneInteractor>,
    private val occlusionInteractor: Lazy<SceneContainerOcclusionInteractor>,
    private val shadeDisplaysRepository: Lazy<ShadeDisplaysRepository>,
) {
) : SceneContainerPlugin {

    private val shadeDisplayId: StateFlow<Int> by lazy { shadeDisplaysRepository.get().displayId }

    /**
     * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled
     * or if the flag value doesn't need to be overridden.
     */
    fun flagValueOverride(@SystemUiStateFlags flag: Long, displayId: Int): Boolean? {
    override fun flagValueOverride(@SystemUiStateFlags flag: Long, displayId: Int): Boolean? {
        if (!SceneContainerFlag.isEnabled) {
            return null
        }
@@ -76,7 +87,7 @@ constructor(
        val invisibleDueToOcclusion = occlusionInteractor.get().invisibleDueToOcclusion.value
        return idleTransitionStateOrNull?.let { idleState ->
            EvaluatorByFlag[flag]?.invoke(
                SceneContainerPluginState(
                SceneContainerPlugin.SceneContainerPluginState(
                    scene = idleState.currentScene,
                    overlays = idleState.currentOverlays,
                    isVisible = sceneInteractor.get().isVisible.value,
@@ -98,7 +109,7 @@ constructor(
         * to be overridden by the scene framework.
         */
        val EvaluatorByFlag =
            mapOf<Long, (SceneContainerPluginState) -> Boolean>(
            mapOf<Long, (SceneContainerPlugin.SceneContainerPluginState) -> Boolean>(
                SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to
                    {
                        when {
@@ -139,11 +150,4 @@ constructor(
                SYSUI_STATE_COMMUNAL_HUB_SHOWING to { it.isVisible && it.scene == Scenes.Communal },
            )
    }

    data class SceneContainerPluginState(
        val scene: SceneKey,
        val overlays: Set<OverlayKey>,
        val invisibleDueToOcclusion: Boolean,
        val isVisible: Boolean,
    )
}
Loading