diff options
| author | 2024-01-03 15:58:35 +0000 | |
|---|---|---|
| committer | 2024-02-20 18:48:29 +0000 | |
| commit | 4a0351795693e6c166b96aa3e19a43ded8cc24eb (patch) | |
| tree | f7c6272f30c2b1d42790fb700e65e5f2b9f20393 | |
| parent | 3fdca447365094bd8a5011226b3e4bd290fb057e (diff) | |
Add Custom Tile tests
Flag: aconfig qs_new_tiles DISABLED
Test: atest CustomTileDataInteractorTest
Test: atest CustomTileMapperTest
Test: atest CustomTileUserActionInteractorTest
Test: atest CustomTileInteractorTest
Bug: 301055700
Change-Id: I43626613fbebe240e8dc16b081a4cda3778c095d
15 files changed, 1161 insertions, 55 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt new file mode 100644 index 000000000000..a5c554406848 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2023 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.qs.tiles.impl.custom.domain.interactor + +import android.content.ComponentName +import android.content.pm.UserInfo +import android.graphics.drawable.Icon +import android.service.quicksettings.Tile +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.external.componentName +import com.android.systemui.qs.external.iQSTileService +import com.android.systemui.qs.external.tileServiceManagerFacade +import com.android.systemui.qs.external.tileServicesFacade +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat +import com.android.systemui.qs.tiles.impl.custom.customTileDefaultsRepository +import com.android.systemui.qs.tiles.impl.custom.customTileInteractor +import com.android.systemui.qs.tiles.impl.custom.customTilePackagesUpdatesRepository +import com.android.systemui.qs.tiles.impl.custom.customTileRepository +import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor +import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults +import com.android.systemui.qs.tiles.impl.custom.tileSpec +import com.android.systemui.testKosmos +import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.user.data.repository.userRepository +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +@OptIn(ExperimentalCoroutinesApi::class) +class CustomTileDataInteractorTest : SysuiTestCase() { + + private val kosmos = + testKosmos().apply { + componentName = TEST_COMPONENT + tileSpec = TileSpec.create(componentName) + } + private val underTest = + with(kosmos) { + CustomTileDataInteractor( + tileSpec = tileSpec, + defaultsRepository = customTileDefaultsRepository, + serviceInteractor = customTileServiceInteractor, + customTileInteractor = customTileInteractor, + packageUpdatesRepository = customTilePackagesUpdatesRepository, + userRepository = userRepository, + tileScope = testScope.backgroundScope, + ) + } + + private suspend fun setup() { + with(kosmos) { + fakeUserRepository.setUserInfos(listOf(TEST_USER_1)) + fakeUserRepository.setSelectedUserInfo(TEST_USER_1) + } + } + + @Test + fun activeTileIsNotBoundUntilDataCollected() = + with(kosmos) { + testScope.runTest { + setup() + customTileRepository.setTileActive(true) + + runCurrent() + + assertThat(iQSTileService.isTileListening).isFalse() + assertThat(tileServiceManagerFacade.isBound).isFalse() + } + } + + @Test + fun notActiveTileIsNotBoundUntilDataCollected() = + with(kosmos) { + testScope.runTest { + setup() + customTileRepository.setTileActive(false) + + runCurrent() + + assertThat(iQSTileService.isTileListening).isFalse() + assertThat(tileServiceManagerFacade.isBound).isFalse() + } + } + + @Test + fun tileIsUnboundWhenDataIsNotListened() = + with(kosmos) { + testScope.runTest { + setup() + customTileRepository.setTileActive(false) + customTileDefaultsRepository.putDefaults( + TEST_USER_1.userHandle, + componentName, + CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label), + ) + val dataJob = + underTest + .tileData(TEST_USER_1.userHandle, flowOf(DataUpdateTrigger.InitialRequest)) + .launchIn(backgroundScope) + runCurrent() + tileServiceManagerFacade.processPendingBind() + assertThat(iQSTileService.isTileListening).isTrue() + assertThat(tileServiceManagerFacade.isBound).isTrue() + + dataJob.cancel() + runCurrent() + + assertThat(iQSTileService.isTileListening).isFalse() + assertThat(tileServiceManagerFacade.isBound).isFalse() + } + } + + @Test + fun tileDataCollection() = + with(kosmos) { + testScope.runTest { + setup() + customTileDefaultsRepository.putDefaults( + TEST_USER_1.userHandle, + componentName, + CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label), + ) + val tileData by + collectLastValue( + underTest.tileData( + TEST_USER_1.userHandle, + flowOf(DataUpdateTrigger.InitialRequest) + ) + ) + runCurrent() + tileServicesFacade.customTileInterface!!.updateTileState(TEST_TILE, 1) + + runCurrent() + + with(tileData!!) { + assertThat(user.identifier).isEqualTo(TEST_USER_1.id) + assertThat(componentName).isEqualTo(componentName) + assertThat(tile).isEqualTo(TEST_TILE) + assertThat(callingAppUid).isEqualTo(1) + assertThat(hasPendingBind).isEqualTo(true) + assertThat(isToggleable).isEqualTo(false) + assertThat(defaultTileIcon).isEqualTo(TEST_TILE.icon) + assertThat(defaultTileLabel).isEqualTo(TEST_TILE.label) + } + } + } + + @Test + fun tileAvailableWhenDefaultsAreLoaded() = + with(kosmos) { + testScope.runTest { + setup() + customTileDefaultsRepository.putDefaults( + TEST_USER_1.userHandle, + tileSpec.componentName, + CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label), + ) + + val isAvailable by collectValues(underTest.availability(TEST_USER_1.userHandle)) + runCurrent() + + assertThat(isAvailable).containsExactlyElementsIn(arrayOf(true)).inOrder() + } + } + + @Test + fun tileUnavailableWhenDefaultsAreNotLoaded() = + with(kosmos) { + testScope.runTest { + setup() + customTileDefaultsRepository.putDefaults( + TEST_USER_1.userHandle, + tileSpec.componentName, + CustomTileDefaults.Error, + ) + + val isAvailable by collectValues(underTest.availability(TEST_USER_1.userHandle)) + runCurrent() + + assertThat(isAvailable).containsExactlyElementsIn(arrayOf(false)).inOrder() + } + } + + @Test + fun tileAvailabilityUndefinedWhenDefaultsAreLoadedForAnotherUser() = + with(kosmos) { + testScope.runTest { + setup() + customTileDefaultsRepository.putDefaults( + TEST_USER_2.userHandle, + tileSpec.componentName, + CustomTileDefaults.Error, + ) + + val isAvailable by collectValues(underTest.availability(TEST_USER_1.userHandle)) + runCurrent() + + assertThat(isAvailable).containsExactlyElementsIn(arrayOf()).inOrder() + } + } + + private companion object { + + val TEST_COMPONENT = ComponentName("test.pkg", "test.cls") + val TEST_USER_1 = UserInfo(1, "first user", UserInfo.FLAG_MAIN) + val TEST_USER_2 = UserInfo(2, "second user", UserInfo.FLAG_MAIN) + val TEST_TILE = + Tile().apply { + label = "test_tile_1" + icon = Icon.createWithContentUri("file://test_1") + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt index 995d6ac66137..9546a32e2a06 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt @@ -25,7 +25,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues -import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.qs.external.TileServiceKey import com.android.systemui.qs.pipeline.shared.TileSpec @@ -35,6 +34,7 @@ import com.android.systemui.qs.tiles.impl.custom.customTileRepository import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults import com.android.systemui.qs.tiles.impl.custom.tileSpec +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first @@ -50,16 +50,16 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) class CustomTileInteractorTest : SysuiTestCase() { - private val kosmos = Kosmos().apply { tileSpec = TileSpec.create(TEST_COMPONENT) } + private val kosmos = testKosmos().apply { tileSpec = TileSpec.create(TEST_COMPONENT) } private val underTest: CustomTileInteractor = with(kosmos) { CustomTileInteractor( - tileSpec, - customTileDefaultsRepository, - customTileRepository, - testScope.backgroundScope, - testScope.testScheduler, + tileSpec = tileSpec, + defaultsRepository = customTileDefaultsRepository, + customTileRepository = customTileRepository, + tileScope = testScope.backgroundScope, + backgroundContext = testScope.testScheduler, ) } @@ -69,14 +69,14 @@ class CustomTileInteractorTest : SysuiTestCase() { testScope.runTest { customTileRepository.setTileActive(true) customTileStatePersister.persistState( - TileServiceKey(TEST_COMPONENT, TEST_USER.identifier), - TEST_TILE, + TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier), + TEST_TILE_1, ) - underTest.initForUser(TEST_USER) + underTest.initForUser(TEST_USER_1) - assertThat(underTest.getTile(TEST_USER)).isEqualTo(TEST_TILE) - assertThat(underTest.getTiles(TEST_USER).first()).isEqualTo(TEST_TILE) + assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1) + assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1) } } @@ -86,18 +86,18 @@ class CustomTileInteractorTest : SysuiTestCase() { testScope.runTest { customTileRepository.setTileActive(false) customTileStatePersister.persistState( - TileServiceKey(TEST_COMPONENT, TEST_USER.identifier), - TEST_TILE, + TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier), + TEST_TILE_1, ) - val tiles = collectValues(underTest.getTiles(TEST_USER)) - val initJob = launch { underTest.initForUser(TEST_USER) } + val tiles = collectValues(underTest.getTiles(TEST_USER_1)) + val initJob = launch { underTest.initForUser(TEST_USER_1) } - underTest.updateTile(TEST_TILE) + underTest.updateTile(TEST_TILE_1) runCurrent() initJob.join() assertThat(tiles()).hasSize(1) - assertThat(tiles().last()).isEqualTo(TEST_TILE) + assertThat(tiles().last()).isEqualTo(TEST_TILE_1) } } @@ -107,34 +107,34 @@ class CustomTileInteractorTest : SysuiTestCase() { testScope.runTest { customTileRepository.setTileActive(false) customTileStatePersister.persistState( - TileServiceKey(TEST_COMPONENT, TEST_USER.identifier), - TEST_TILE, + TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier), + TEST_TILE_1, ) - val tiles = collectValues(underTest.getTiles(TEST_USER)) - val initJob = launch { underTest.initForUser(TEST_USER) } + val tiles = collectValues(underTest.getTiles(TEST_USER_1)) + val initJob = launch { underTest.initForUser(TEST_USER_1) } - customTileDefaultsRepository.putDefaults(TEST_USER, TEST_COMPONENT, TEST_DEFAULTS) - customTileDefaultsRepository.requestNewDefaults(TEST_USER, TEST_COMPONENT) + customTileDefaultsRepository.putDefaults(TEST_USER_1, TEST_COMPONENT, TEST_DEFAULTS) + customTileDefaultsRepository.requestNewDefaults(TEST_USER_1, TEST_COMPONENT) runCurrent() initJob.join() assertThat(tiles()).hasSize(1) - assertThat(tiles().last()).isEqualTo(TEST_TILE) + assertThat(tiles().last()).isEqualTo(TEST_TILE_1) } } @Test(expected = IllegalStateException::class) fun getTileBeforeInitThrows() = - with(kosmos) { testScope.runTest { underTest.getTile(TEST_USER) } } + with(kosmos) { testScope.runTest { underTest.getTile(TEST_USER_1) } } @Test fun initSuspendsForActiveTileNotRestoredAndNotUpdated() = with(kosmos) { testScope.runTest { customTileRepository.setTileActive(true) - val tiles = collectValues(underTest.getTiles(TEST_USER)) + val tiles = collectValues(underTest.getTiles(TEST_USER_1)) - val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER) } + val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER_1) } advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS) // Is still suspended @@ -149,12 +149,12 @@ class CustomTileInteractorTest : SysuiTestCase() { testScope.runTest { customTileRepository.setTileActive(false) customTileStatePersister.persistState( - TileServiceKey(TEST_COMPONENT, TEST_USER.identifier), - TEST_TILE, + TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier), + TEST_TILE_1, ) - val tiles = collectValues(underTest.getTiles(TEST_USER)) + val tiles = collectValues(underTest.getTiles(TEST_USER_1)) - val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER) } + val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER_1) } advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS) // Is still suspended @@ -176,18 +176,89 @@ class CustomTileInteractorTest : SysuiTestCase() { } } + @Test + fun activeFollowsTheRepository() { + with(kosmos) { + testScope.runTest { + customTileRepository.setTileActive(false) + assertThat(underTest.isTileActive()).isFalse() + + customTileRepository.setTileActive(true) + assertThat(underTest.isTileActive()).isTrue() + } + } + } + + @Test + fun initForTheSameUserProcessedOnce() = + with(kosmos) { + testScope.runTest { + customTileRepository.setTileActive(false) + customTileStatePersister.persistState( + TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier), + TEST_TILE_1, + ) + val tiles = collectValues(underTest.getTiles(TEST_USER_1)) + val initJob = launch { + underTest.initForUser(TEST_USER_1) + underTest.initForUser(TEST_USER_1) + } + + underTest.updateTile(TEST_TILE_1) + runCurrent() + initJob.join() + + assertThat(tiles()).hasSize(1) + assertThat(tiles().last()).isEqualTo(TEST_TILE_1) + } + } + + @Test + fun initForDifferentUsersProcessedOnce() = + with(kosmos) { + testScope.runTest { + customTileRepository.setTileActive(true) + customTileStatePersister.persistState( + TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier), + TEST_TILE_1, + ) + customTileStatePersister.persistState( + TileServiceKey(TEST_COMPONENT, TEST_USER_2.identifier), + TEST_TILE_2, + ) + val tiles1 by collectValues(underTest.getTiles(TEST_USER_1)) + val tiles2 by collectValues(underTest.getTiles(TEST_USER_2)) + + val initJob = launch { + underTest.initForUser(TEST_USER_1) + underTest.initForUser(TEST_USER_2) + } + runCurrent() + initJob.join() + + assertThat(tiles1).isEmpty() + assertThat(tiles2).hasSize(1) + assertThat(tiles2.last()).isEqualTo(TEST_TILE_2) + } + } + private companion object { val TEST_COMPONENT = ComponentName("test.pkg", "test.cls") - val TEST_USER = UserHandle.of(1)!! - val TEST_TILE by lazy { + val TEST_USER_1 = UserHandle.of(1)!! + val TEST_USER_2 = UserHandle.of(2)!! + val TEST_TILE_1 by lazy { Tile().apply { label = "test_tile_1" icon = Icon.createWithContentUri("file://test_1") } } - val TEST_DEFAULTS by lazy { - CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label) + val TEST_TILE_2 by lazy { + Tile().apply { + label = "test_tile_2" + icon = Icon.createWithContentUri("file://test_2") + } } + val TEST_DEFAULTS by lazy { CustomTileDefaults.Result(TEST_TILE_1.icon, TEST_TILE_1.label) } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt new file mode 100644 index 000000000000..a2127a4717ce --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2023 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.qs.tiles.impl.custom.domain.interactor + +import android.app.IUriGrantsManager +import android.content.ComponentName +import android.graphics.drawable.Drawable +import android.graphics.drawable.Icon +import android.graphics.drawable.TestStubDrawable +import android.os.UserHandle +import android.service.quicksettings.Tile +import android.widget.Button +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject.Companion.assertThat +import com.android.systemui.qs.tiles.impl.custom.customTileQsTileConfig +import com.android.systemui.qs.tiles.impl.custom.domain.CustomTileMapper +import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel +import com.android.systemui.qs.tiles.impl.custom.tileSpec +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CustomTileMapperTest : SysuiTestCase() { + + private val uriGrantsManager: IUriGrantsManager = mock {} + private val kosmos = testKosmos().apply { tileSpec = TileSpec.Companion.create(TEST_COMPONENT) } + private val underTest by lazy { + CustomTileMapper( + context = mock { whenever(createContextAsUser(any(), any())).thenReturn(context) }, + uriGrantsManager = uriGrantsManager, + ) + } + + @Test + fun stateHasPendingBinding() = + with(kosmos) { + testScope.runTest { + val actual = + underTest.map( + customTileQsTileConfig, + createModel(hasPendingBind = true), + ) + val expected = + createTileState( + activationState = QSTileState.ActivationState.UNAVAILABLE, + actions = setOf(QSTileState.UserAction.LONG_CLICK), + ) + + assertThat(actual).isEqualTo(expected) + } + } + + @Test + fun stateActive() = + with(kosmos) { + testScope.runTest { + val actual = + underTest.map( + customTileQsTileConfig, + createModel(tileState = Tile.STATE_ACTIVE), + ) + val expected = + createTileState( + activationState = QSTileState.ActivationState.ACTIVE, + ) + + assertThat(actual).isEqualTo(expected) + } + } + + @Test + fun stateInactive() = + with(kosmos) { + testScope.runTest { + val actual = + underTest.map( + customTileQsTileConfig, + createModel(tileState = Tile.STATE_INACTIVE), + ) + val expected = + createTileState( + activationState = QSTileState.ActivationState.INACTIVE, + ) + + assertThat(actual).isEqualTo(expected) + } + } + + @Test + fun stateUnavailable() = + with(kosmos) { + testScope.runTest { + val actual = + underTest.map( + customTileQsTileConfig, + createModel(tileState = Tile.STATE_UNAVAILABLE), + ) + val expected = + createTileState( + activationState = QSTileState.ActivationState.UNAVAILABLE, + actions = setOf(QSTileState.UserAction.LONG_CLICK), + ) + + assertThat(actual).isEqualTo(expected) + } + } + + @Test + fun tileWithChevron() = + with(kosmos) { + testScope.runTest { + val actual = + underTest.map( + customTileQsTileConfig, + createModel(isToggleable = false), + ) + val expected = + createTileState( + sideIcon = QSTileState.SideViewIcon.Chevron, + a11yClass = Button::class.qualifiedName, + ) + + assertThat(actual).isEqualTo(expected) + } + } + + @Test + fun defaultIconFallback() = + with(kosmos) { + testScope.runTest { + val actual = + underTest.map( + customTileQsTileConfig, + createModel(tileIcon = createIcon(RuntimeException(), false)), + ) + val expected = + createTileState( + activationState = QSTileState.ActivationState.INACTIVE, + icon = DEFAULT_DRAWABLE, + ) + + assertThat(actual).isEqualTo(expected) + } + } + + @Test + fun failedToLoadIconTileIsInactive() = + with(kosmos) { + testScope.runTest { + val actual = + underTest.map( + customTileQsTileConfig, + createModel( + tileIcon = createIcon(RuntimeException(), false), + defaultTileIcon = createIcon(null, true) + ), + ) + val expected = + createTileState( + icon = null, + activationState = QSTileState.ActivationState.INACTIVE, + ) + + assertThat(actual).isEqualTo(expected) + } + } + + private fun Kosmos.createModel( + tileState: Int = Tile.STATE_ACTIVE, + tileIcon: Icon = createIcon(DRAWABLE, false), + hasPendingBind: Boolean = false, + isToggleable: Boolean = true, + defaultTileIcon: Icon = createIcon(DEFAULT_DRAWABLE, true), + ) = + CustomTileDataModel( + UserHandle.of(1), + tileSpec.componentName, + Tile().apply { + state = tileState + label = "test label" + subtitle = "test subtitle" + icon = tileIcon + contentDescription = "test content description" + }, + callingAppUid = 0, + hasPendingBind = hasPendingBind, + isToggleable = isToggleable, + defaultTileLabel = "test default tile label", + defaultTileIcon = defaultTileIcon, + ) + + private fun createIcon(drawable: Drawable?, isDefault: Boolean): Icon = mock { + if (isDefault) { + whenever(loadDrawable(any())).thenReturn(drawable) + } else { + whenever(loadDrawableCheckingUriGrant(any(), any(), any(), any())).thenReturn(drawable) + } + } + + private fun createIcon(exception: RuntimeException, isDefault: Boolean): Icon = mock { + if (isDefault) { + whenever(loadDrawable(any())).thenThrow(exception) + } else { + whenever(loadDrawableCheckingUriGrant(any(), eq(uriGrantsManager), any(), any())) + .thenThrow(exception) + } + } + + private fun createTileState( + activationState: QSTileState.ActivationState = QSTileState.ActivationState.ACTIVE, + icon: Drawable? = DRAWABLE, + sideIcon: QSTileState.SideViewIcon = QSTileState.SideViewIcon.None, + actions: Set<QSTileState.UserAction> = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + a11yClass: String? = Switch::class.qualifiedName, + ): QSTileState { + return QSTileState( + { icon?.let { com.android.systemui.common.shared.model.Icon.Loaded(icon, null) } }, + "test label", + activationState, + "test subtitle", + actions, + "test content description", + null, + sideIcon, + QSTileState.EnabledState.ENABLED, + a11yClass, + ) + } + + private companion object { + val TEST_COMPONENT = ComponentName("test.pkg", "test.cls") + + val DEFAULT_DRAWABLE = TestStubDrawable("default_icon_drawable") + val DRAWABLE = TestStubDrawable("icon_drawable") + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..c709f16c3213 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2023 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.qs.tiles.impl.custom.domain.interactor + +import android.app.PendingIntent +import android.content.ComponentName +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.content.pm.UserInfo +import android.graphics.drawable.Icon +import android.provider.Settings +import android.service.quicksettings.Tile +import android.service.quicksettings.TileService +import android.view.IWindowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.external.componentName +import com.android.systemui.qs.external.iQSTileService +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.intentInputs +import com.android.systemui.qs.tiles.base.actions.pendingIntentInputs +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick +import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor +import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel +import com.android.systemui.qs.tiles.impl.custom.qsTileLogger +import com.android.systemui.qs.tiles.impl.custom.tileSpec +import com.android.systemui.testKosmos +import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CustomTileUserActionInteractorTest : SysuiTestCase() { + + private val inputHandler = FakeQSTileIntentUserInputHandler() + private val packageManagerFacade = FakePackageManagerFacade() + private val windowManagerFacade = FakeWindowManagerFacade() + private val kosmos = + testKosmos().apply { + componentName = TEST_COMPONENT + tileSpec = TileSpec.create(componentName) + testCase = this@CustomTileUserActionInteractorTest + } + + private val underTest = + with(kosmos) { + CustomTileUserActionInteractor( + context = + mock { + whenever(packageManager).thenReturn(packageManagerFacade.packageManager) + }, + tileSpec = tileSpec, + qsTileLogger = qsTileLogger, + windowManager = windowManagerFacade.windowManager, + displayTracker = mock {}, + qsTileIntentUserInputHandler = inputHandler, + backgroundContext = testDispatcher, + serviceInteractor = customTileServiceInteractor, + ) + } + + private suspend fun setup() { + with(kosmos) { + fakeUserRepository.setUserInfos(listOf(TEST_USER_1)) + fakeUserRepository.setSelectedUserInfo(TEST_USER_1) + } + } + + @Test + fun clickStartsActivityWhenPossible() = + with(kosmos) { + testScope.runTest { + setup() + underTest.handleInput( + click(customTileModel(activityLaunchForClick = pendingIntent())) + ) + + assertThat(windowManagerFacade.isTokenGranted).isTrue() + assertThat(inputHandler.pendingIntentInputs).hasSize(1) + assertThat(iQSTileService.clicks).hasSize(0) + } + } + + @Test + fun clickPassedToTheServiceWhenNoActivity() = + with(kosmos) { + testScope.runTest { + setup() + packageManagerFacade.resolutionResult = null + underTest.handleInput(click(customTileModel(activityLaunchForClick = null))) + + assertThat(windowManagerFacade.isTokenGranted).isTrue() + assertThat(inputHandler.pendingIntentInputs).hasSize(0) + assertThat(iQSTileService.clicks).hasSize(1) + } + } + + @Test + fun longClickOpensResolvedIntent() = + with(kosmos) { + testScope.runTest { + setup() + packageManagerFacade.resolutionResult = + ActivityInfo().apply { + packageName = "resolved.pkg" + name = "Test" + } + underTest.handleInput(longClick(customTileModel())) + + assertThat(inputHandler.intentInputs).hasSize(1) + with(inputHandler.intentInputs.first()) { + assertThat(intent.action).isEqualTo(TileService.ACTION_QS_TILE_PREFERENCES) + assertThat(intent.component).isEqualTo(ComponentName("resolved.pkg", "Test")) + assertThat( + intent.getParcelableExtra( + Intent.EXTRA_COMPONENT_NAME, + ComponentName::class.java + ) + ) + .isEqualTo(componentName) + assertThat(intent.getIntExtra(TileService.EXTRA_STATE, Int.MAX_VALUE)) + .isEqualTo(111) + } + } + } + + @Test + fun longClickOpensDefaultIntentWhenNoResolved() = + with(kosmos) { + testScope.runTest { + setup() + underTest.handleInput(longClick(customTileModel())) + + assertThat(inputHandler.intentInputs).hasSize(1) + with(inputHandler.intentInputs.first()) { + assertThat(intent.action) + .isEqualTo(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + assertThat(intent.data.toString()).isEqualTo("package:test.pkg") + } + } + } + + @Test + fun revokeTokenDoesntRevokeWhenShowingDialog() = + with(kosmos) { + testScope.runTest { + setup() + underTest.handleInput(click(customTileModel())) + underTest.setShowingDialog(true) + + underTest.revokeToken(false) + + assertThat(windowManagerFacade.isTokenGranted).isTrue() + } + } + + @Test + fun forceRevokeTokenRevokesWhenShowingDialog() = + with(kosmos) { + testScope.runTest { + setup() + underTest.handleInput(click(customTileModel())) + underTest.setShowingDialog(true) + + underTest.revokeToken(true) + + assertThat(windowManagerFacade.isTokenGranted).isFalse() + } + } + + @Test + fun revokeTokenRevokesWhenNotShowingDialog() = + with(kosmos) { + testScope.runTest { + setup() + underTest.handleInput(click(customTileModel())) + underTest.setShowingDialog(false) + + underTest.revokeToken(false) + + assertThat(windowManagerFacade.isTokenGranted).isFalse() + } + } + + @Test + fun startActivityDoesntStartWithNoToken() = + with(kosmos) { + testScope.runTest { + setup() + underTest.startActivityAndCollapse(mock()) + + // Checking all types of inputs + assertThat(inputHandler.handledInputs).isEmpty() + } + } + + private fun pendingIntent(): PendingIntent = mock { whenever(isActivity).thenReturn(true) } + + private fun Kosmos.customTileModel( + componentName: ComponentName = tileSpec.componentName, + activityLaunchForClick: PendingIntent? = null, + tileState: Int = 111, + ) = + CustomTileDataModel( + TEST_USER_1.userHandle, + componentName, + Tile().also { + it.activityLaunchForClick = activityLaunchForClick + it.state = tileState + }, + callingAppUid = 0, + hasPendingBind = false, + isToggleable = false, + defaultTileLabel = "default_label", + defaultTileIcon = Icon.createWithContentUri("default_icon"), + ) + + private class FakePackageManagerFacade(val packageManager: PackageManager = mock()) { + + var resolutionResult: ActivityInfo? = null + + init { + whenever(packageManager.resolveActivityAsUser(any(), any<Int>(), any())).then { + ResolveInfo().apply { activityInfo = resolutionResult } + } + } + } + + private class FakeWindowManagerFacade(val windowManager: IWindowManager = mock()) { + + var isTokenGranted: Boolean = false + private set + + init { + with(windowManager) { + whenever(removeWindowToken(any(), any())).then { + isTokenGranted = false + Unit + } + whenever(addWindowToken(any(), any(), any(), nullable())).then { + isTokenGranted = true + Unit + } + } + } + } + + private companion object { + + val TEST_COMPONENT = ComponentName("test.pkg", "test.cls") + val TEST_USER_1 = UserInfo(1, "first user", UserInfo.FLAG_MAIN) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt index cff95d8368a2..1b3e58524815 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt @@ -68,8 +68,7 @@ constructor( serviceInteractor.setUser(user) // Wait for the CustomTileInteractor to become initialized first, because - // binding - // the service might access it + // binding the service might access it customTileInteractor.initForUser(user) // Bind the TileService for not active tile serviceInteractor.bindOnStart() diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt index fd96fc5b6693..3e507cda4805 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt @@ -77,6 +77,13 @@ constructor( suspend fun isTileToggleable(): Boolean = customTileRepository.isTileToggleable() /** + * True if the tile is active and false the otherwise. This effectively is a value of the + * [android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE]. This is not the same as + * [Tile.STATE_ACTIVE]. + */ + suspend fun isTileActive(): Boolean = customTileRepository.isTileActive() + + /** * Initializes the repository for the current user. Suspends until it's safe to call [getTile] * which needs at least one of the following: * - defaults are loaded; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt index acff40f816a9..79e903c7bce9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt @@ -58,7 +58,7 @@ constructor( private val activityStarter: ActivityStarter, private val userActionInteractor: Lazy<CustomTileUserActionInteractor>, private val customTileInteractor: CustomTileInteractor, - private val userRepository: UserRepository, + userRepository: UserRepository, private val qsTileLogger: QSTileLogger, private val tileServices: TileServices, @QSTileScope private val tileScope: CoroutineScope, @@ -78,10 +78,10 @@ constructor( get() = tileReceivingInterface.mutableRefreshEvents /** Clears all pending binding for an active tile and binds not active one. */ - fun bindOnStart() { + suspend fun bindOnStart() { try { with(getTileServiceManager()) { - if (isActiveTile) { + if (customTileInteractor.isTileActive()) { clearPendingBind() } else { setBindRequested(true) @@ -94,10 +94,10 @@ constructor( } /** Binds active tile WITHOUT CLEARING pending binds. */ - fun bindOnClick() { + suspend fun bindOnClick() { try { with(getTileServiceManager()) { - if (isActiveTile) { + if (customTileInteractor.isTileActive()) { setBindRequested(true) tileServiceInterface.onStartListening() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt index c3e1feaa6dfe..a16ac360e7e4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt @@ -77,7 +77,7 @@ constructor( qsTileLogger.logCustomTileUserActionDelivered(tileSpec) } - private fun click( + private suspend fun click( view: View?, activityLaunchForClick: PendingIntent?, ) { @@ -114,9 +114,6 @@ constructor( } fun startActivityAndCollapse(pendingIntent: PendingIntent) { - if (!pendingIntent.isActivity) { - return - } if (!isTokenGranted) { return } diff --git a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt index b88f302cdfdd..1a9f4b40c179 100644 --- a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt +++ b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt @@ -24,12 +24,27 @@ import android.graphics.PixelFormat * Stub drawable that does nothing. It's to be used in tests as a mock drawable and checked for the * same instance */ -class TestStubDrawable : Drawable() { +class TestStubDrawable(private val name: String? = null) : Drawable() { override fun draw(canvas: Canvas) = Unit override fun setAlpha(alpha: Int) = Unit override fun setColorFilter(colorFilter: ColorFilter?) = Unit override fun getOpacity(): Int = PixelFormat.UNKNOWN - override fun equals(other: Any?): Boolean = this === other + override fun toString(): String { + return name ?: super.toString() + } + + override fun getConstantState(): ConstantState = + TestStubConstantState(this, changingConfigurations) + + private class TestStubConstantState( + private val drawable: Drawable, + private val changingConfigurations: Int, + ) : ConstantState() { + + override fun newDrawable(): Drawable = drawable + + override fun getChangingConfigurations(): Int = changingConfigurations + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt new file mode 100644 index 000000000000..cff59807e00f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 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.qs.external + +import android.os.Binder +import android.os.IBinder +import android.service.quicksettings.IQSTileService + +class FakeIQSTileService : IQSTileService { + + var isTileAdded: Boolean = false + private set + var isTileListening: Boolean = false + private set + var isUnlockComplete: Boolean = false + val clicks: List<IBinder?> + get() = mutableClicks + + private val mutableClicks: MutableList<IBinder?> = mutableListOf() + private val binder = Binder() + + override fun asBinder(): IBinder = binder + + override fun onTileAdded() { + isTileAdded = true + } + + override fun onTileRemoved() { + isTileAdded = false + } + + override fun onStartListening() { + isTileListening = true + } + + override fun onStopListening() { + isTileListening = false + } + + override fun onClick(wtoken: IBinder?) { + mutableClicks.add(wtoken) + } + + override fun onUnlockComplete() { + isUnlockComplete = true + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServiceManagerFacade.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServiceManagerFacade.kt new file mode 100644 index 000000000000..101335f38531 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServiceManagerFacade.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 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.qs.external + +import android.service.quicksettings.IQSTileService +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever + +// TODO(b/299909989) Make a fake instead +class FakeTileServiceManagerFacade( + private val iQSTileService: IQSTileService, + val tileServiceManager: TileServiceManager = mock {}, +) { + + private var hasPendingBind: Boolean = false + + var isBound: Boolean = false + private set + + init { + with(tileServiceManager) { + whenever(tileService).thenReturn(iQSTileService) + whenever(setBindRequested(any())).then { + val isRequested: Boolean = it.getArgument(0) + hasPendingBind = isRequested + if (!isRequested) { + isBound = false + } + Unit + } + whenever(clearPendingBind()).then { + hasPendingBind = false + Unit + } + whenever(hasPendingBind()).then { hasPendingBind } + } + } + + fun processPendingBind() { + if (hasPendingBind) { + isBound = true + } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServicesFacade.kt index f8ce707b0bb2..0975e55295e4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServicesFacade.kt @@ -16,9 +16,24 @@ package com.android.systemui.qs.external -import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever -/** Returns mocks */ -var Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by - Kosmos.Fixture { TileLifecycleManager.Factory { _, _ -> mock<TileLifecycleManager>() } } +class FakeTileServicesFacade( + private val TileServiceManager: TileServiceManager, + val tileServices: TileServices = mock {} +) { + + var customTileInterface: CustomTileInterface? = null + private set + + init { + with(tileServices) { + whenever(getTileWrapper(any())).then { + customTileInterface = it.getArgument(0) + TileServiceManager + } + } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt new file mode 100644 index 000000000000..36c2c2b6eb23 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 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.qs.external + +import android.content.ComponentName +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.componentName: ComponentName by Kosmos.Fixture() + +/** Returns mocks */ +var Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by Kosmos.Fixture { mock {} } + +val Kosmos.iQSTileService: FakeIQSTileService by Kosmos.Fixture { FakeIQSTileService() } +val Kosmos.tileServiceManagerFacade: FakeTileServiceManagerFacade by + Kosmos.Fixture { FakeTileServiceManagerFacade(iQSTileService) } + +val Kosmos.tileServiceManager: TileServiceManager by + Kosmos.Fixture { tileServiceManagerFacade.tileServiceManager } + +val Kosmos.tileServicesFacade: FakeTileServicesFacade by + Kosmos.Fixture { (FakeTileServicesFacade(tileServiceManager)) } +val Kosmos.tileServices: TileServices by Kosmos.Fixture { tileServicesFacade.tileServices } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt index 14f28fedc5e4..561e2540d465 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt @@ -17,19 +17,47 @@ package com.android.systemui.qs.tiles.impl.custom import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.activityStarter import com.android.systemui.qs.external.FakeCustomTileStatePersister +import com.android.systemui.qs.external.tileServices import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.logging.QSTileLogger import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTilePackageUpdatesRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.FakePackageManagerAdapterFacade +import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileInteractor +import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileServiceInteractor +import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileUserActionInteractor +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder +import com.android.systemui.user.data.repository.userRepository +import com.android.systemui.util.mockito.mock var Kosmos.tileSpec: TileSpec.CustomTileSpec by Kosmos.Fixture() +var Kosmos.customTileQsTileConfig: QSTileConfig by + Kosmos.Fixture { QSTileConfigTestBuilder.build { tileSpec = this@Fixture.tileSpec } } +val Kosmos.qsTileLogger: QSTileLogger by Kosmos.Fixture { mock {} } + val Kosmos.customTileStatePersister: FakeCustomTileStatePersister by Kosmos.Fixture { FakeCustomTileStatePersister() } +val Kosmos.customTileInteractor: CustomTileInteractor by + Kosmos.Fixture { + CustomTileInteractor( + tileSpec, + customTileDefaultsRepository, + customTileRepository, + testScope.backgroundScope, + testScope.testScheduler, + ) + } + val Kosmos.customTileRepository: FakeCustomTileRepository by Kosmos.Fixture { FakeCustomTileRepository( @@ -48,3 +76,31 @@ val Kosmos.customTilePackagesUpdatesRepository: FakeCustomTilePackageUpdatesRepo val Kosmos.packageManagerAdapterFacade: FakePackageManagerAdapterFacade by Kosmos.Fixture { FakePackageManagerAdapterFacade(tileSpec.componentName) } + +val Kosmos.customTileServiceInteractor: CustomTileServiceInteractor by + Kosmos.Fixture { + CustomTileServiceInteractor( + tileSpec, + activityStarter, + { customTileUserActionInteractor }, + customTileInteractor, + userRepository, + qsTileLogger, + tileServices, + testScope.backgroundScope, + ) + } + +val Kosmos.customTileUserActionInteractor: CustomTileUserActionInteractor by + Kosmos.Fixture { + CustomTileUserActionInteractor( + testCase.context, + tileSpec, + qsTileLogger, + mock {}, + mock {}, + FakeQSTileIntentUserInputHandler(), + testDispatcher, + customTileServiceInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt index 9d0faca94fb4..4f5c9b48690d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt @@ -70,7 +70,7 @@ private constructor(failureMetadata: FailureMetadata, subject: QSTileState?) : } /** Shortcut for `Truth.assertAbout(states()).that(state)`. */ - fun assertThat(state: QSTileState?): QSTileStateSubject = - Truth.assertAbout(states()).that(state) + fun assertThat(actual: QSTileState?): QSTileStateSubject = + Truth.assertAbout(states()).that(actual) } } |