blob: 9b6aef76d579d44302cced4280b8eaab0f6388d0 [file] [log] [blame]
/*
* Copyright (C) 2022 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.statusbar.pipeline.wifi.ui.viewmodel
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.yield
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class WifiViewModelTest : SysuiTestCase() {
private lateinit var underTest: WifiViewModel
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var logger: ConnectivityPipelineLogger
@Mock private lateinit var constants: WifiConstants
private lateinit var connectivityRepository: FakeConnectivityRepository
private lateinit var wifiRepository: FakeWifiRepository
private lateinit var interactor: WifiInteractor
private lateinit var scope: CoroutineScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
interactor = WifiInteractor(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
createAndSetViewModel()
}
@After
fun tearDown() {
scope.cancel()
}
// Note on testing: [WifiViewModel] exposes 3 different instances of
// [LocationBasedWifiViewModel]. In practice, these 3 different instances will get the exact
// same data for icon, activity, etc. flows. So, most of these tests will test just one of the
// instances. There are also some tests that verify all 3 instances received the same data.
@Test
fun wifiIcon_notEnabled_outputsNull() = runBlocking(IMMEDIATE) {
wifiRepository.setIsWifiEnabled(false)
// Start as non-null so we can verify we got the update
var latest: Icon? = Icon.Resource(0, null)
val job = underTest
.home
.wifiIcon
.onEach { latest = it }
.launchIn(this)
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
yield()
assertThat(latest).isNull()
job.cancel()
}
@Test
fun wifiIcon_forceHidden_outputsNull() = runBlocking(IMMEDIATE) {
connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
// Start as non-null so we can verify we got the update
var latest: Icon? = Icon.Resource(0, null)
val job = underTest
.home
.wifiIcon
.onEach { latest = it }
.launchIn(this)
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
yield()
assertThat(latest).isNull()
job.cancel()
}
@Test
fun wifiIcon_notForceHidden_outputsVisible() = runBlocking(IMMEDIATE) {
connectivityRepository.setForceHiddenIcons(setOf())
var latest: Icon? = null
val job = underTest
.home
.wifiIcon
.onEach { latest = it }
.launchIn(this)
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
yield()
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
job.cancel()
}
@Test
fun wifiIcon_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) {
var latest: Icon? = null
val job = underTest
.home
.wifiIcon
.onEach { latest = it }
.launchIn(this)
wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
yield()
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
val icon = latest as Icon.Resource
assertThat(icon.res).isEqualTo(WIFI_NO_NETWORK)
assertThat(icon.contentDescription?.getAsString())
.contains(context.getString(WIFI_NO_CONNECTION))
assertThat(icon.contentDescription?.getAsString())
.contains(context.getString(NO_INTERNET))
job.cancel()
}
@Test
fun wifiIcon_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
var latest: Icon? = Icon.Resource(0, null)
val job = underTest
.home
.wifiIcon
.onEach { latest = it }
.launchIn(this)
wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
yield()
assertThat(latest).isNull()
job.cancel()
}
@Test
fun wifiIcon_isActiveNullLevel_outputsNull() = runBlocking(IMMEDIATE) {
var latest: Icon? = Icon.Resource(0, null)
val job = underTest
.home
.wifiIcon
.onEach { latest = it }
.launchIn(this)
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null))
yield()
assertThat(latest).isNull()
job.cancel()
}
@Test
fun wifiIcon_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) {
var latest: Icon? = null
val job = underTest
.home
.wifiIcon
.onEach { latest = it }
.launchIn(this)
val level = 1
wifiRepository.setWifiNetwork(
WifiNetworkModel.Active(
NETWORK_ID,
isValidated = true,
level,
)
)
yield()
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
val icon = latest as Icon.Resource
assertThat(icon.res).isEqualTo(WIFI_FULL_ICONS[level])
assertThat(icon.contentDescription?.getAsString())
.contains(context.getString(WIFI_CONNECTION_STRENGTH[level]))
assertThat(icon.contentDescription?.getAsString())
.doesNotContain(context.getString(NO_INTERNET))
job.cancel()
}
@Test
fun wifiIcon_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) {
var latest: Icon? = null
val job = underTest
.home
.wifiIcon
.onEach { latest = it }
.launchIn(this)
val level = 4
wifiRepository.setWifiNetwork(
WifiNetworkModel.Active(
NETWORK_ID,
isValidated = false,
level,
)
)
yield()
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
val icon = latest as Icon.Resource
assertThat(icon.res).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
assertThat(icon.contentDescription?.getAsString())
.contains(context.getString(WIFI_CONNECTION_STRENGTH[level]))
assertThat(icon.contentDescription?.getAsString())
.contains(context.getString(NO_INTERNET))
job.cancel()
}
@Test
fun wifiIcon_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) {
var latestHome: Icon? = null
val jobHome = underTest
.home
.wifiIcon
.onEach { latestHome = it }
.launchIn(this)
var latestKeyguard: Icon? = null
val jobKeyguard = underTest
.keyguard
.wifiIcon
.onEach { latestKeyguard = it }
.launchIn(this)
var latestQs: Icon? = null
val jobQs = underTest
.qs
.wifiIcon
.onEach { latestQs = it }
.launchIn(this)
wifiRepository.setWifiNetwork(
WifiNetworkModel.Active(
NETWORK_ID,
isValidated = true,
level = 1
)
)
yield()
assertThat(latestHome).isInstanceOf(Icon.Resource::class.java)
assertThat(latestHome).isEqualTo(latestKeyguard)
assertThat(latestKeyguard).isEqualTo(latestQs)
jobHome.cancel()
jobKeyguard.cancel()
jobQs.cancel()
}
@Test
fun activity_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(false)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var activityIn: Boolean? = null
val activityInJob = underTest
.home
.isActivityInViewVisible
.onEach { activityIn = it }
.launchIn(this)
var activityOut: Boolean? = null
val activityOutJob = underTest
.home
.isActivityOutViewVisible
.onEach { activityOut = it }
.launchIn(this)
var activityContainer: Boolean? = null
val activityContainerJob = underTest
.home
.isActivityContainerVisible
.onEach { activityContainer = it }
.launchIn(this)
// Verify that on launch, we receive false.
assertThat(activityIn).isFalse()
assertThat(activityOut).isFalse()
assertThat(activityContainer).isFalse()
activityInJob.cancel()
activityOutJob.cancel()
activityContainerJob.cancel()
}
@Test
fun activity_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(false)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var activityIn: Boolean? = null
val activityInJob = underTest
.home
.isActivityInViewVisible
.onEach { activityIn = it }
.launchIn(this)
var activityOut: Boolean? = null
val activityOutJob = underTest
.home
.isActivityOutViewVisible
.onEach { activityOut = it }
.launchIn(this)
var activityContainer: Boolean? = null
val activityContainerJob = underTest
.home
.isActivityContainerVisible
.onEach { activityContainer = it }
.launchIn(this)
// WHEN we update the repo to have activity
val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
wifiRepository.setWifiActivity(activity)
yield()
// THEN we didn't update to the new activity (because our config is false)
assertThat(activityIn).isFalse()
assertThat(activityOut).isFalse()
assertThat(activityContainer).isFalse()
activityInJob.cancel()
activityOutJob.cancel()
activityContainerJob.cancel()
}
@Test
fun activity_nullSsid_outputsFalse() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, ssid = null))
var activityIn: Boolean? = null
val activityInJob = underTest
.home
.isActivityInViewVisible
.onEach { activityIn = it }
.launchIn(this)
var activityOut: Boolean? = null
val activityOutJob = underTest
.home
.isActivityOutViewVisible
.onEach { activityOut = it }
.launchIn(this)
var activityContainer: Boolean? = null
val activityContainerJob = underTest
.home
.isActivityContainerVisible
.onEach { activityContainer = it }
.launchIn(this)
// WHEN we update the repo to have activity
val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
wifiRepository.setWifiActivity(activity)
yield()
// THEN we still output false because our network's SSID is null
assertThat(activityIn).isFalse()
assertThat(activityOut).isFalse()
assertThat(activityContainer).isFalse()
activityInJob.cancel()
activityOutJob.cancel()
activityContainerJob.cancel()
}
@Test
fun activity_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latestHome: Boolean? = null
val jobHome = underTest
.home
.isActivityInViewVisible
.onEach { latestHome = it }
.launchIn(this)
var latestKeyguard: Boolean? = null
val jobKeyguard = underTest
.keyguard
.isActivityInViewVisible
.onEach { latestKeyguard = it }
.launchIn(this)
var latestQs: Boolean? = null
val jobQs = underTest
.qs
.isActivityInViewVisible
.onEach { latestQs = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latestHome).isTrue()
assertThat(latestKeyguard).isTrue()
assertThat(latestQs).isTrue()
jobHome.cancel()
jobKeyguard.cancel()
jobQs.cancel()
}
@Test
fun activityIn_hasActivityInTrue_outputsTrue() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
.home
.isActivityInViewVisible
.onEach { latest = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun activityIn_hasActivityInFalse_outputsFalse() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
.home
.isActivityInViewVisible
.onEach { latest = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun activityOut_hasActivityOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
.home
.isActivityOutViewVisible
.onEach { latest = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun activityOut_hasActivityOutFalse_outputsFalse() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
.home
.isActivityOutViewVisible
.onEach { latest = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun activityContainer_hasActivityInTrue_outputsTrue() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
.home
.isActivityContainerVisible
.onEach { latest = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun activityContainer_hasActivityOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
.home
.isActivityContainerVisible
.onEach { latest = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun activityContainer_inAndOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
.home
.isActivityContainerVisible
.onEach { latest = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun activityContainer_inAndOutFalse_outputsFalse() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
.home
.isActivityContainerVisible
.onEach { latest = it }
.launchIn(this)
val activity = WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
wifiRepository.setWifiActivity(activity)
yield()
assertThat(latest).isFalse()
job.cancel()
}
private fun createAndSetViewModel() {
// [WifiViewModel] creates its flows as soon as it's instantiated, and some of those flow
// creations rely on certain config values that we mock out in individual tests. This method
// allows tests to create the view model only after those configs are correctly set up.
underTest = WifiViewModel(
constants,
context,
logger,
interactor,
scope,
statusBarPipelineFlags,
)
}
private fun ContentDescription.getAsString(): String? {
return when (this) {
is ContentDescription.Loaded -> this.description
is ContentDescription.Resource -> context.getString(this.res)
}
}
companion object {
private const val NETWORK_ID = 2
private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active(NETWORK_ID, ssid = "AB")
}
}
private val IMMEDIATE = Dispatchers.Main.immediate