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

Commit 84820f19 authored by Anton Potapov's avatar Anton Potapov Committed by Android (Google) Code Review
Browse files

Merge "Add Custom Tile tests" into main

parents cc30a3f5 4a035179
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