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

Commit 3c52fb7c authored by Steve Elliott's avatar Steve Elliott
Browse files

[kairos] Kairos in mobile pipeline view models

Flag: com.android.systemui.status_bar_mobile_icon_kairos
Bug: 383172066
Test: atest
Change-Id: Id0653289a5bbf3eb3ce91391375e53d644ea8374
parent 47059222
Loading
Loading
Loading
Loading
+103 −120
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 * Copyright (C) 2024 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.
@@ -19,110 +19,69 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fake
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.kairos.ActivatedKairosFixture
import com.android.systemui.kairos.ExperimentalKairosApi
import com.android.systemui.kairos.KairosTestScope
import com.android.systemui.kairos.kairos
import com.android.systemui.kairos.runKairosTest
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepositoryKairos
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairosImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairos
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractorKairos
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.mock

@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@OptIn(ExperimentalKairosApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class LocationBasedMobileIconViewModelKairosTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private lateinit var commonImpl: MobileIconViewModelCommonKairos
    private lateinit var homeIcon: HomeMobileIconViewModelKairos
    private lateinit var qsIcon: QsMobileIconViewModelKairos
    private lateinit var keyguardIcon: KeyguardMobileIconViewModelKairos
    private lateinit var iconsInteractor: MobileIconsInteractor
    private lateinit var interactor: MobileIconInteractor
    private val connectionsRepository = kosmos.fakeMobileConnectionsRepository
    private lateinit var repository: FakeMobileConnectionRepository
    private lateinit var airplaneModeInteractor: AirplaneModeInteractor

    private val connectivityRepository = FakeConnectivityRepository()
    private val flags =
        FakeFeatureFlagsClassic().also {
            it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
        }

    @Mock private lateinit var constants: ConnectivityConstants
    private val tableLogBuffer =
        logcatTableLogBuffer(kosmos, "LocationBasedMobileIconViewModelTest")
    @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker

    private val testDispatcher = UnconfinedTestDispatcher()
    private val testScope = TestScope(testDispatcher)

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        airplaneModeInteractor =
            AirplaneModeInteractor(
                FakeAirplaneModeRepository(),
                FakeConnectivityRepository(),
                connectionsRepository,
    private val Kosmos.commonImpl: MobileIconViewModelKairosCommon by ActivatedKairosFixture {
        MobileIconViewModelKairos(
            SUB_1_ID,
            interactor,
            airplaneModeInteractor,
            constants,
            featureFlagsClassic,
        )
        repository =
            FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
                isInService.value = true
                cdmaLevel.value = 1
                primaryLevel.value = 1
                isEmergencyOnly.value = false
                numberOfLevels.value = 4
                resolvedNetworkType.value = ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G")
                dataConnectionState.value = DataConnectionState.Connected
    }

        connectionsRepository.activeMobileDataRepository.value = repository
    private val Kosmos.homeIcon: HomeMobileIconViewModelKairos by
        Kosmos.Fixture { HomeMobileIconViewModelKairos(commonImpl, mock()) }

        connectivityRepository.apply { setMobileConnected() }
    private val Kosmos.qsIcon: QsMobileIconViewModelKairos by
        Kosmos.Fixture { QsMobileIconViewModelKairos(commonImpl) }

        iconsInteractor =
            MobileIconsInteractorImpl(
                connectionsRepository,
                carrierConfigTracker,
                tableLogBuffer,
                connectivityRepository,
                FakeUserSetupRepository(),
                testScope.backgroundScope,
                context,
                flags,
            )
    private val Kosmos.keyguardIcon: KeyguardMobileIconViewModelKairos by
        Kosmos.Fixture { KeyguardMobileIconViewModelKairos(commonImpl) }

    private val Kosmos.iconsInteractor: MobileIconsInteractorKairos
        get() = mobileIconsInteractorKairos

        interactor =
            MobileIconInteractorImpl(
                testScope.backgroundScope,
    private val Kosmos.interactor: MobileIconInteractorKairos by
        Kosmos.Fixture {
            MobileIconInteractorKairosImpl(
                iconsInteractor.activeDataConnectionHasDataEnabled,
                iconsInteractor.alwaysShowDataRatIcon,
                iconsInteractor.alwaysUseCdmaLevel,
@@ -136,32 +95,60 @@ class LocationBasedMobileIconViewModelKairosTest : SysuiTestCase() {
                context,
                MobileIconCarrierIdOverridesFake(),
            )
        }

        commonImpl =
            MobileIconViewModelKairos(
                SUB_1_ID,
                interactor,
                airplaneModeInteractor,
                constants,
                testScope.backgroundScope,
    private val Kosmos.repository: FakeMobileConnectionRepositoryKairos by
        Kosmos.Fixture {
            FakeMobileConnectionRepositoryKairos(SUB_1_ID, kairos, tableLogBuffer).apply {
                isInService.setValue(true)
                cdmaLevel.setValue(1)
                primaryLevel.setValue(1)
                isEmergencyOnly.setValue(false)
                numberOfLevels.setValue(4)
                resolvedNetworkType.setValue(
                    ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G")
                )
                dataConnectionState.setValue(DataConnectionState.Connected)
            }
        }

        homeIcon = HomeMobileIconViewModelKairos(commonImpl, mock())
        qsIcon = QsMobileIconViewModelKairos(commonImpl)
        keyguardIcon = KeyguardMobileIconViewModelKairos(commonImpl)
    private val Kosmos.constants: ConnectivityConstants by Kosmos.Fixture { mock() }
    private val Kosmos.tableLogBuffer by
        Kosmos.Fixture { logcatTableLogBuffer(this, "LocationBasedMobileIconViewModelTest") }

    private val kosmos =
        testKosmos().apply {
            useUnconfinedTestDispatcher()
            mobileConnectionsRepositoryKairos =
                fakeMobileConnectionsRepositoryKairos.apply {
                    setActiveMobileDataSubscriptionId(SUB_1_ID)
                    subscriptions.setValue(
                        listOf(
                            SubscriptionModel(
                                SUB_1_ID,
                                carrierName = "carrierName",
                                profileClass = 0,
                            )
                        )
                    )
                }
            connectivityRepository.fake.apply { setMobileConnected() }
            featureFlagsClassic.fake.apply {
                set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
            }
        }

    @Test
    fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() =
        testScope.runTest {
            var latestHome: SignalIconModel? = null
            val homeJob = homeIcon.icon.onEach { latestHome = it }.launchIn(this)
    private fun runTest(block: suspend KairosTestScope.() -> Unit) =
        kosmos.run { runKairosTest { block() } }

            var latestQs: SignalIconModel? = null
            val qsJob = qsIcon.icon.onEach { latestQs = it }.launchIn(this)
    @Test
    fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() = runTest {
        repository.dataEnabled.setValue(true)
        repository.isInService.setValue(true)

            var latestKeyguard: SignalIconModel? = null
            val keyguardJob = keyguardIcon.icon.onEach { latestKeyguard = it }.launchIn(this)
        val latestHome by homeIcon.icon.collectLastValue()
        val latestQs by qsIcon.icon.collectLastValue()
        val latestKeyguard by keyguardIcon.icon.collectLastValue()

        var expected = defaultSignal(level = 1)

@@ -175,10 +162,6 @@ class LocationBasedMobileIconViewModelKairosTest : SysuiTestCase() {
        assertThat(latestHome).isEqualTo(expected)
        assertThat(latestQs).isEqualTo(expected)
        assertThat(latestKeyguard).isEqualTo(expected)

            homeJob.cancel()
            qsJob.cancel()
            keyguardJob.cancel()
    }

    companion object {
+649 −795

File changed.

Preview size limit exceeded, changes collapsed.

+250 −275

File changed.

Preview size limit exceeded, changes collapsed.

+48 −41
Original line number Diff line number Diff line
@@ -21,81 +21,83 @@ import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fake
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.kairos.ExperimentalKairosApi
import com.android.systemui.kairos.KairosTestScope
import com.android.systemui.kairos.runKairosTest
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.statusbar.core.NewStatusBarIcons
import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalKairosApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class StackedMobileIconViewModelKairosTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope

    private val Kosmos.underTest: StackedMobileIconViewModelKairos by Fixture {
        stackedMobileIconViewModelKairos
    private val kosmos =
        testKosmos().useUnconfinedTestDispatcher().apply {
            mobileConnectionsRepositoryKairos = fakeMobileConnectionsRepositoryKairos
            featureFlagsClassic.fake.apply {
                setDefault(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS)
            }

    @Before
    fun setUp() {
        kosmos.underTest.activateIn(testScope)
        }

    private val Kosmos.underTest
        get() = stackedMobileIconViewModelKairos

    @Test
    @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
    fun dualSim_filtersOutNonDualConnections() =
        kosmos.runTest {
            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf()
        kosmos.runKairosTest {
            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf())
            assertThat(underTest.dualSim).isNull()

            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1))
            assertThat(underTest.dualSim).isNull()

            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
                listOf(SUB_1, SUB_2, SUB_3)
            )
            assertThat(underTest.dualSim).isNull()

            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
            assertThat(underTest.dualSim).isNotNull()
        }

    @Test
    @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
    fun dualSim_filtersOutNonCellularIcons() =
        kosmos.runTest {
            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
        kosmos.runKairosTest {
            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1))
            assertThat(underTest.dualSim).isNull()

            fakeMobileIconsInteractor
                .getInteractorForSubId(SUB_1.subscriptionId)!!
                .signalLevelIcon
                .value =
                SignalIconModel.Satellite(
                    level = 0,
                    icon = Icon.Resource(res = 0, contentDescription = null),
                )
            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
            mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
                .sample()[SUB_1.subscriptionId]!!
                .apply {
                    isNonTerrestrial.setValue(true)
                    satelliteLevel.setValue(0)
                }

            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
            assertThat(underTest.dualSim).isNull()
        }

    @Test
    @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
    fun dualSim_tracksActiveSubId() =
        kosmos.runTest {
        kosmos.runKairosTest {
            // Active sub id is null, order is unchanged
            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
            setIconLevel(SUB_1.subscriptionId, 1)
            setIconLevel(SUB_2.subscriptionId, 2)

@@ -103,16 +105,21 @@ class StackedMobileIconViewModelKairosTest : SysuiTestCase() {
            assertThat(underTest.dualSim!!.secondary.level).isEqualTo(2)

            // Active sub is 2, order is swapped
            fakeMobileIconsInteractor.activeMobileDataSubscriptionId.value = SUB_2.subscriptionId
            mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(
                SUB_2.subscriptionId
            )

            assertThat(underTest.dualSim!!.primary.level).isEqualTo(2)
            assertThat(underTest.dualSim!!.secondary.level).isEqualTo(1)
        }

    private fun setIconLevel(subId: Int, level: Int) {
        with(kosmos.fakeMobileIconsInteractor.getInteractorForSubId(subId)!!) {
            signalLevelIcon.value =
                (signalLevelIcon.value as SignalIconModel.Cellular).copy(level = level)
    private suspend fun KairosTestScope.setIconLevel(subId: Int, level: Int) {
        mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId.sample()[subId]!!.apply {
            isNonTerrestrial.setValue(false)
            isInService.setValue(true)
            inflateSignalStrength.setValue(false)
            isGsm.setValue(true)
            primaryLevel.setValue(level)
        }
    }

+2 −2
Original line number Diff line number Diff line
@@ -45,8 +45,8 @@ class StackedMobileIconViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope

    private val Kosmos.underTest: StackedMobileIconViewModel by Fixture {
        stackedMobileIconViewModel
    private val Kosmos.underTest: StackedMobileIconViewModelImpl by Fixture {
        stackedMobileIconViewModelImpl
    }

    @Before
Loading