blob: 496f090da25cca7e07429b1157a372bac41ed328 [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.shared.data.repository
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
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
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class ConnectivityRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: ConnectivityRepositoryImpl
@Mock private lateinit var connectivitySlots: ConnectivitySlots
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var logger: ConnectivityInputLogger
private lateinit var scope: CoroutineScope
@Mock private lateinit var tunerService: TunerService
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
scope = CoroutineScope(IMMEDIATE)
underTest =
ConnectivityRepositoryImpl(
connectivitySlots,
context,
dumpManager,
logger,
scope,
tunerService,
)
}
@After
fun tearDown() {
scope.cancel()
}
@Test
fun forceHiddenSlots_initiallyGetsDefault() =
runBlocking(IMMEDIATE) {
setUpEthernetWifiMobileSlotNames()
context
.getOrCreateTestableResources()
.addOverride(DEFAULT_HIDDEN_ICONS_RESOURCE, arrayOf(SLOT_WIFI, SLOT_ETHERNET))
// Re-create our [ConnectivityRepositoryImpl], since it fetches
// config_statusBarIconsToExclude when it's first constructed
underTest =
ConnectivityRepositoryImpl(
connectivitySlots,
context,
dumpManager,
logger,
scope,
tunerService,
)
var latest: Set<ConnectivitySlot>? = null
val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
job.cancel()
}
@Test
fun forceHiddenSlots_slotNamesAdded_flowHasSlots() =
runBlocking(IMMEDIATE) {
setUpEthernetWifiMobileSlotNames()
var latest: Set<ConnectivitySlot>? = null
val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
job.cancel()
}
@Test
fun forceHiddenSlots_wrongKey_doesNotUpdate() =
runBlocking(IMMEDIATE) {
setUpEthernetWifiMobileSlotNames()
var latest: Set<ConnectivitySlot>? = null
val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
// WHEN onTuningChanged with the wrong key
getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
yield()
// THEN we didn't update our value and still have the old one
assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
job.cancel()
}
@Test
fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() =
runBlocking(IMMEDIATE) {
setUpEthernetWifiMobileSlotNames()
context
.getOrCreateTestableResources()
.addOverride(DEFAULT_HIDDEN_ICONS_RESOURCE, arrayOf(SLOT_WIFI, SLOT_ETHERNET))
// Re-create our [ConnectivityRepositoryImpl], since it fetches
// config_statusBarIconsToExclude when it's first constructed
underTest =
ConnectivityRepositoryImpl(
connectivitySlots,
context,
dumpManager,
logger,
scope,
tunerService,
)
var latest: Set<ConnectivitySlot>? = null
val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
// First, update the slots
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
// WHEN we update to a null value
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
yield()
// THEN we go back to our default value
assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
job.cancel()
}
@Test
fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() =
runBlocking(IMMEDIATE) {
var latest: Set<ConnectivitySlot>? = null
val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
job.cancel()
}
@Test
fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() =
runBlocking(IMMEDIATE) {
setUpEthernetWifiMobileSlotNames()
var latest: Set<ConnectivitySlot>? = null
val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
// WHEN there's empty and blank slot names
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE, ,,$SLOT_WIFI")
// THEN we skip that slot but still process the other ones
assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
job.cancel()
}
@Test
fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() =
runBlocking(IMMEDIATE) {
var latest: Set<ConnectivitySlot>? = null
val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
getTunable()
.onTuningChanged(
HIDDEN_ICONS_TUNABLE_KEY,
"$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
)
assertThat(latest).isEmpty()
job.cancel()
}
@Test
fun forceHiddenSlots_newSubscriberGetsCurrentValue() =
runBlocking(IMMEDIATE) {
setUpEthernetWifiMobileSlotNames()
var latest1: Set<ConnectivitySlot>? = null
val job1 = underTest.forceHiddenSlots.onEach { latest1 = it }.launchIn(this)
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
// WHEN we add a second subscriber after having already emitted a value
var latest2: Set<ConnectivitySlot>? = null
val job2 = underTest.forceHiddenSlots.onEach { latest2 = it }.launchIn(this)
// THEN the second subscribe receives the already-emitted value
assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
job1.cancel()
job2.cancel()
}
private fun getTunable(): TunerService.Tunable {
val callbackCaptor = argumentCaptor<TunerService.Tunable>()
Mockito.verify(tunerService).addTunable(callbackCaptor.capture(), any())
return callbackCaptor.value!!
}
private fun setUpEthernetWifiMobileSlotNames() {
whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET))
.thenReturn(ConnectivitySlot.ETHERNET)
whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(ConnectivitySlot.MOBILE)
}
companion object {
private const val SLOT_ETHERNET = "ethernet"
private const val SLOT_WIFI = "wifi"
private const val SLOT_MOBILE = "mobile"
private val IMMEDIATE = Dispatchers.Main.immediate
}
}