1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.pipeline.mobile.domain.interactor 18 19 import android.telephony.CellSignalStrength 20 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN 21 import androidx.test.filters.SmallTest 22 import com.android.settingslib.SignalIcon.MobileIconGroup 23 import com.android.settingslib.mobile.TelephonyIcons 24 import com.android.systemui.SysuiTestCase 25 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState 26 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel 27 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.CarrierMergedNetworkType 28 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType 29 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType 30 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository 31 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE 32 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G 33 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.THREE_G 34 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy 35 import com.android.systemui.util.mockito.mock 36 import com.google.common.truth.Truth.assertThat 37 import kotlinx.coroutines.CoroutineScope 38 import kotlinx.coroutines.Dispatchers 39 import kotlinx.coroutines.flow.launchIn 40 import kotlinx.coroutines.flow.onEach 41 import kotlinx.coroutines.runBlocking 42 import kotlinx.coroutines.yield 43 import org.junit.Before 44 import org.junit.Test 45 46 @SmallTest 47 class MobileIconInteractorTest : SysuiTestCase() { 48 private lateinit var underTest: MobileIconInteractor 49 private val mobileMappingsProxy = FakeMobileMappingsProxy() 50 private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock()) 51 private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID, mock()) 52 53 private val scope = CoroutineScope(IMMEDIATE) 54 55 @Before setUpnull56 fun setUp() { 57 underTest = 58 MobileIconInteractorImpl( 59 scope, 60 mobileIconsInteractor.activeDataConnectionHasDataEnabled, 61 mobileIconsInteractor.alwaysShowDataRatIcon, 62 mobileIconsInteractor.alwaysUseCdmaLevel, 63 mobileIconsInteractor.defaultMobileNetworkConnectivity, 64 mobileIconsInteractor.defaultMobileIconMapping, 65 mobileIconsInteractor.defaultMobileIconGroup, 66 mobileIconsInteractor.defaultDataSubId, 67 mobileIconsInteractor.isDefaultConnectionFailed, 68 mobileIconsInteractor.isForceHidden, 69 connectionRepository, 70 ) 71 } 72 73 @Test gsm_level_default_unknownnull74 fun gsm_level_default_unknown() = 75 runBlocking(IMMEDIATE) { 76 connectionRepository.isGsm.value = true 77 78 var latest: Int? = null 79 val job = underTest.level.onEach { latest = it }.launchIn(this) 80 81 assertThat(latest).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) 82 83 job.cancel() 84 } 85 86 @Test gsm_usesGsmLevelnull87 fun gsm_usesGsmLevel() = 88 runBlocking(IMMEDIATE) { 89 connectionRepository.isGsm.value = true 90 connectionRepository.primaryLevel.value = GSM_LEVEL 91 connectionRepository.cdmaLevel.value = CDMA_LEVEL 92 93 var latest: Int? = null 94 val job = underTest.level.onEach { latest = it }.launchIn(this) 95 96 assertThat(latest).isEqualTo(GSM_LEVEL) 97 98 job.cancel() 99 } 100 101 @Test gsm_alwaysShowCdmaTrue_stillUsesGsmLevelnull102 fun gsm_alwaysShowCdmaTrue_stillUsesGsmLevel() = 103 runBlocking(IMMEDIATE) { 104 connectionRepository.isGsm.value = true 105 connectionRepository.primaryLevel.value = GSM_LEVEL 106 connectionRepository.cdmaLevel.value = CDMA_LEVEL 107 mobileIconsInteractor.alwaysUseCdmaLevel.value = true 108 109 var latest: Int? = null 110 val job = underTest.level.onEach { latest = it }.launchIn(this) 111 112 assertThat(latest).isEqualTo(GSM_LEVEL) 113 114 job.cancel() 115 } 116 117 @Test notGsm_level_default_unknownnull118 fun notGsm_level_default_unknown() = 119 runBlocking(IMMEDIATE) { 120 connectionRepository.isGsm.value = false 121 122 var latest: Int? = null 123 val job = underTest.level.onEach { latest = it }.launchIn(this) 124 125 assertThat(latest).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) 126 job.cancel() 127 } 128 129 @Test notGsm_alwaysShowCdmaTrue_usesCdmaLevelnull130 fun notGsm_alwaysShowCdmaTrue_usesCdmaLevel() = 131 runBlocking(IMMEDIATE) { 132 connectionRepository.isGsm.value = false 133 connectionRepository.primaryLevel.value = GSM_LEVEL 134 connectionRepository.cdmaLevel.value = CDMA_LEVEL 135 mobileIconsInteractor.alwaysUseCdmaLevel.value = true 136 137 var latest: Int? = null 138 val job = underTest.level.onEach { latest = it }.launchIn(this) 139 140 assertThat(latest).isEqualTo(CDMA_LEVEL) 141 142 job.cancel() 143 } 144 145 @Test notGsm_alwaysShowCdmaFalse_usesPrimaryLevelnull146 fun notGsm_alwaysShowCdmaFalse_usesPrimaryLevel() = 147 runBlocking(IMMEDIATE) { 148 connectionRepository.isGsm.value = false 149 connectionRepository.primaryLevel.value = GSM_LEVEL 150 connectionRepository.cdmaLevel.value = CDMA_LEVEL 151 mobileIconsInteractor.alwaysUseCdmaLevel.value = false 152 153 var latest: Int? = null 154 val job = underTest.level.onEach { latest = it }.launchIn(this) 155 156 assertThat(latest).isEqualTo(GSM_LEVEL) 157 158 job.cancel() 159 } 160 161 @Test numberOfLevels_comesFromReponull162 fun numberOfLevels_comesFromRepo() = 163 runBlocking(IMMEDIATE) { 164 var latest: Int? = null 165 val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this) 166 167 connectionRepository.numberOfLevels.value = 5 168 assertThat(latest).isEqualTo(5) 169 170 connectionRepository.numberOfLevels.value = 4 171 assertThat(latest).isEqualTo(4) 172 173 job.cancel() 174 } 175 176 @Test iconGroup_three_gnull177 fun iconGroup_three_g() = 178 runBlocking(IMMEDIATE) { 179 connectionRepository.resolvedNetworkType.value = 180 DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G)) 181 182 var latest: MobileIconGroup? = null 183 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 184 185 assertThat(latest).isEqualTo(TelephonyIcons.THREE_G) 186 187 job.cancel() 188 } 189 190 @Test iconGroup_updates_on_changenull191 fun iconGroup_updates_on_change() = 192 runBlocking(IMMEDIATE) { 193 connectionRepository.resolvedNetworkType.value = 194 DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G)) 195 196 var latest: MobileIconGroup? = null 197 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 198 199 connectionRepository.resolvedNetworkType.value = 200 DefaultNetworkType(mobileMappingsProxy.toIconKey(FOUR_G)) 201 yield() 202 203 assertThat(latest).isEqualTo(TelephonyIcons.FOUR_G) 204 205 job.cancel() 206 } 207 208 @Test iconGroup_5g_override_typenull209 fun iconGroup_5g_override_type() = 210 runBlocking(IMMEDIATE) { 211 connectionRepository.resolvedNetworkType.value = 212 OverrideNetworkType(mobileMappingsProxy.toIconKeyOverride(FIVE_G_OVERRIDE)) 213 214 var latest: MobileIconGroup? = null 215 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 216 217 assertThat(latest).isEqualTo(TelephonyIcons.NR_5G) 218 219 job.cancel() 220 } 221 222 @Test iconGroup_default_if_no_lookupnull223 fun iconGroup_default_if_no_lookup() = 224 runBlocking(IMMEDIATE) { 225 connectionRepository.resolvedNetworkType.value = 226 DefaultNetworkType(mobileMappingsProxy.toIconKey(NETWORK_TYPE_UNKNOWN)) 227 228 var latest: MobileIconGroup? = null 229 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 230 231 assertThat(latest).isEqualTo(FakeMobileIconsInteractor.DEFAULT_ICON) 232 233 job.cancel() 234 } 235 236 @Test iconGroup_carrierMerged_usesOverridenull237 fun iconGroup_carrierMerged_usesOverride() = 238 runBlocking(IMMEDIATE) { 239 connectionRepository.resolvedNetworkType.value = CarrierMergedNetworkType 240 241 var latest: MobileIconGroup? = null 242 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 243 244 assertThat(latest).isEqualTo(CarrierMergedNetworkType.iconGroupOverride) 245 246 job.cancel() 247 } 248 249 @Test icon group - checks default datanull250 fun `icon group - checks default data`() = 251 runBlocking(IMMEDIATE) { 252 mobileIconsInteractor.defaultDataSubId.value = SUB_1_ID 253 connectionRepository.resolvedNetworkType.value = 254 DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G)) 255 256 var latest: MobileIconGroup? = null 257 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 258 259 assertThat(latest).isEqualTo(TelephonyIcons.THREE_G) 260 261 // Default data sub id changes to something else 262 mobileIconsInteractor.defaultDataSubId.value = 123 263 yield() 264 265 assertThat(latest).isEqualTo(TelephonyIcons.NOT_DEFAULT_DATA) 266 267 job.cancel() 268 } 269 270 @Test alwaysShowDataRatIcon_matchesParentnull271 fun alwaysShowDataRatIcon_matchesParent() = 272 runBlocking(IMMEDIATE) { 273 var latest: Boolean? = null 274 val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this) 275 276 mobileIconsInteractor.alwaysShowDataRatIcon.value = true 277 assertThat(latest).isTrue() 278 279 mobileIconsInteractor.alwaysShowDataRatIcon.value = false 280 assertThat(latest).isFalse() 281 282 job.cancel() 283 } 284 285 @Test alwaysUseCdmaLevel_matchesParentnull286 fun alwaysUseCdmaLevel_matchesParent() = 287 runBlocking(IMMEDIATE) { 288 var latest: Boolean? = null 289 val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this) 290 291 mobileIconsInteractor.alwaysUseCdmaLevel.value = true 292 assertThat(latest).isTrue() 293 294 mobileIconsInteractor.alwaysUseCdmaLevel.value = false 295 assertThat(latest).isFalse() 296 297 job.cancel() 298 } 299 300 @Test test_isDefaultDataEnabled_matchesParentnull301 fun test_isDefaultDataEnabled_matchesParent() = 302 runBlocking(IMMEDIATE) { 303 var latest: Boolean? = null 304 val job = underTest.isDefaultDataEnabled.onEach { latest = it }.launchIn(this) 305 306 mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true 307 assertThat(latest).isTrue() 308 309 mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = false 310 assertThat(latest).isFalse() 311 312 job.cancel() 313 } 314 315 @Test test_isDefaultConnectionFailed_matchedParentnull316 fun test_isDefaultConnectionFailed_matchedParent() = 317 runBlocking(IMMEDIATE) { 318 val job = underTest.isDefaultConnectionFailed.launchIn(this) 319 320 mobileIconsInteractor.isDefaultConnectionFailed.value = false 321 assertThat(underTest.isDefaultConnectionFailed.value).isFalse() 322 323 mobileIconsInteractor.isDefaultConnectionFailed.value = true 324 assertThat(underTest.isDefaultConnectionFailed.value).isTrue() 325 326 job.cancel() 327 } 328 329 @Test dataState_connectednull330 fun dataState_connected() = 331 runBlocking(IMMEDIATE) { 332 var latest: Boolean? = null 333 val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this) 334 335 connectionRepository.dataConnectionState.value = DataConnectionState.Connected 336 yield() 337 338 assertThat(latest).isTrue() 339 340 job.cancel() 341 } 342 343 @Test dataState_notConnectednull344 fun dataState_notConnected() = 345 runBlocking(IMMEDIATE) { 346 var latest: Boolean? = null 347 val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this) 348 349 connectionRepository.dataConnectionState.value = DataConnectionState.Disconnected 350 351 assertThat(latest).isFalse() 352 353 job.cancel() 354 } 355 356 @Test isInService - uses repository valuenull357 fun `isInService - uses repository value`() = 358 runBlocking(IMMEDIATE) { 359 var latest: Boolean? = null 360 val job = underTest.isInService.onEach { latest = it }.launchIn(this) 361 362 connectionRepository.isInService.value = true 363 364 assertThat(latest).isTrue() 365 366 connectionRepository.isInService.value = false 367 368 assertThat(latest).isFalse() 369 370 job.cancel() 371 } 372 373 @Test roaming - is gsm - uses connection modelnull374 fun `roaming - is gsm - uses connection model`() = 375 runBlocking(IMMEDIATE) { 376 var latest: Boolean? = null 377 val job = underTest.isRoaming.onEach { latest = it }.launchIn(this) 378 379 connectionRepository.cdmaRoaming.value = true 380 connectionRepository.isGsm.value = true 381 connectionRepository.isRoaming.value = false 382 yield() 383 384 assertThat(latest).isFalse() 385 386 connectionRepository.isRoaming.value = true 387 yield() 388 389 assertThat(latest).isTrue() 390 391 job.cancel() 392 } 393 394 @Test roaming - is cdma - uses cdma roaming bitnull395 fun `roaming - is cdma - uses cdma roaming bit`() = 396 runBlocking(IMMEDIATE) { 397 var latest: Boolean? = null 398 val job = underTest.isRoaming.onEach { latest = it }.launchIn(this) 399 400 connectionRepository.cdmaRoaming.value = false 401 connectionRepository.isGsm.value = false 402 connectionRepository.isRoaming.value = true 403 yield() 404 405 assertThat(latest).isFalse() 406 407 connectionRepository.cdmaRoaming.value = true 408 connectionRepository.isGsm.value = false 409 connectionRepository.isRoaming.value = false 410 yield() 411 412 assertThat(latest).isTrue() 413 414 job.cancel() 415 } 416 417 @Test roaming - false while carrierNetworkChangeActivenull418 fun `roaming - false while carrierNetworkChangeActive`() = 419 runBlocking(IMMEDIATE) { 420 var latest: Boolean? = null 421 val job = underTest.isRoaming.onEach { latest = it }.launchIn(this) 422 423 connectionRepository.cdmaRoaming.value = true 424 connectionRepository.isGsm.value = false 425 connectionRepository.isRoaming.value = true 426 connectionRepository.carrierNetworkChangeActive.value = true 427 yield() 428 429 assertThat(latest).isFalse() 430 431 connectionRepository.cdmaRoaming.value = true 432 connectionRepository.isGsm.value = true 433 yield() 434 435 assertThat(latest).isFalse() 436 437 job.cancel() 438 } 439 440 @Test network name - uses operatorAlphaShot when non null and repo is defaultnull441 fun `network name - uses operatorAlphaShot when non null and repo is default`() = 442 runBlocking(IMMEDIATE) { 443 var latest: NetworkNameModel? = null 444 val job = underTest.networkName.onEach { latest = it }.launchIn(this) 445 446 val testOperatorName = "operatorAlphaShort" 447 448 // Default network name, operator name is non-null, uses the operator name 449 connectionRepository.networkName.value = DEFAULT_NAME 450 connectionRepository.operatorAlphaShort.value = testOperatorName 451 yield() 452 453 assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived(testOperatorName)) 454 455 // Default network name, operator name is null, uses the default 456 connectionRepository.operatorAlphaShort.value = null 457 yield() 458 459 assertThat(latest).isEqualTo(DEFAULT_NAME) 460 461 // Derived network name, operator name non-null, uses the derived name 462 connectionRepository.networkName.value = DERIVED_NAME 463 connectionRepository.operatorAlphaShort.value = testOperatorName 464 yield() 465 466 assertThat(latest).isEqualTo(DERIVED_NAME) 467 468 job.cancel() 469 } 470 471 @Test isForceHidden_matchesParentnull472 fun isForceHidden_matchesParent() = 473 runBlocking(IMMEDIATE) { 474 var latest: Boolean? = null 475 val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this) 476 477 mobileIconsInteractor.isForceHidden.value = true 478 assertThat(latest).isTrue() 479 480 mobileIconsInteractor.isForceHidden.value = false 481 assertThat(latest).isFalse() 482 483 job.cancel() 484 } 485 486 companion object { 487 private val IMMEDIATE = Dispatchers.Main.immediate 488 489 private const val GSM_LEVEL = 1 490 private const val CDMA_LEVEL = 2 491 492 private const val SUB_1_ID = 1 493 494 private val DEFAULT_NAME = NetworkNameModel.Default("test default name") 495 private val DERIVED_NAME = NetworkNameModel.IntentDerived("test derived name") 496 } 497 } 498