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

Commit 4a035179 authored by Anton Potapov's avatar Anton Potapov
Browse files

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
parent 3fdca447
Loading
Loading
Loading
Loading
+241 −0
Original line number Diff line number Diff line
/*
 * 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")
            }
    }
}
+107 −36
Original line number Diff line number Diff line
@@ -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) }
    }
}
+265 −0

File added.

Preview size limit exceeded, changes collapsed.

+283 −0

File added.

Preview size limit exceeded, changes collapsed.

+1 −2
Original line number Diff line number Diff line
@@ -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()
Loading