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.platform.test.annotations.EnableFlags 20 import android.telephony.CellSignalStrength 21 import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET 22 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN 23 import androidx.test.filters.SmallTest 24 import com.android.settingslib.mobile.MobileIconCarrierIdOverrides 25 import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl 26 import com.android.settingslib.mobile.TelephonyIcons 27 import com.android.systemui.SysuiTestCase 28 import com.android.systemui.coroutines.collectLastValue 29 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState 30 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel 31 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.CarrierMergedNetworkType 32 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType 33 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType 34 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel 35 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository 36 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE 37 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G 38 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.THREE_G 39 import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel 40 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel 41 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy 42 import com.android.systemui.util.mockito.any 43 import com.android.systemui.util.mockito.mock 44 import com.android.systemui.util.mockito.whenever 45 import com.google.common.truth.Truth.assertThat 46 import kotlinx.coroutines.ExperimentalCoroutinesApi 47 import kotlinx.coroutines.flow.MutableStateFlow 48 import kotlinx.coroutines.flow.launchIn 49 import kotlinx.coroutines.flow.onEach 50 import kotlinx.coroutines.test.TestScope 51 import kotlinx.coroutines.test.UnconfinedTestDispatcher 52 import kotlinx.coroutines.test.runTest 53 import org.junit.Before 54 import org.junit.Test 55 import org.mockito.ArgumentMatchers.anyInt 56 import org.mockito.ArgumentMatchers.anyString 57 58 @OptIn(ExperimentalCoroutinesApi::class) 59 @SmallTest 60 class MobileIconInteractorTest : SysuiTestCase() { 61 private lateinit var underTest: MobileIconInteractor 62 private val mobileMappingsProxy = FakeMobileMappingsProxy() 63 private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock()) 64 65 private val subscriptionModel = 66 MutableStateFlow( 67 SubscriptionModel( 68 subscriptionId = SUB_1_ID, 69 carrierName = DEFAULT_NAME, 70 profileClass = PROFILE_CLASS_UNSET, 71 ) 72 ) 73 74 private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID, mock()) 75 76 private val testDispatcher = UnconfinedTestDispatcher() 77 private val testScope = TestScope(testDispatcher) 78 79 @Before setUpnull80 fun setUp() { 81 underTest = createInteractor() 82 83 mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true 84 connectionRepository.isInService.value = true 85 } 86 87 @Test gsm_usesGsmLevelnull88 fun gsm_usesGsmLevel() = 89 testScope.runTest { 90 connectionRepository.isGsm.value = true 91 connectionRepository.primaryLevel.value = GSM_LEVEL 92 connectionRepository.cdmaLevel.value = CDMA_LEVEL 93 94 var latest: Int? = null 95 val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this) 96 97 assertThat(latest).isEqualTo(GSM_LEVEL) 98 99 job.cancel() 100 } 101 102 @Test gsm_alwaysShowCdmaTrue_stillUsesGsmLevelnull103 fun gsm_alwaysShowCdmaTrue_stillUsesGsmLevel() = 104 testScope.runTest { 105 connectionRepository.isGsm.value = true 106 connectionRepository.primaryLevel.value = GSM_LEVEL 107 connectionRepository.cdmaLevel.value = CDMA_LEVEL 108 mobileIconsInteractor.alwaysUseCdmaLevel.value = true 109 110 var latest: Int? = null 111 val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this) 112 113 assertThat(latest).isEqualTo(GSM_LEVEL) 114 115 job.cancel() 116 } 117 118 @Test notGsm_level_default_unknownnull119 fun notGsm_level_default_unknown() = 120 testScope.runTest { 121 connectionRepository.isGsm.value = false 122 123 var latest: Int? = null 124 val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this) 125 126 assertThat(latest).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) 127 job.cancel() 128 } 129 130 @Test notGsm_alwaysShowCdmaTrue_usesCdmaLevelnull131 fun notGsm_alwaysShowCdmaTrue_usesCdmaLevel() = 132 testScope.runTest { 133 connectionRepository.isGsm.value = false 134 connectionRepository.primaryLevel.value = GSM_LEVEL 135 connectionRepository.cdmaLevel.value = CDMA_LEVEL 136 mobileIconsInteractor.alwaysUseCdmaLevel.value = true 137 138 var latest: Int? = null 139 val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this) 140 141 assertThat(latest).isEqualTo(CDMA_LEVEL) 142 143 job.cancel() 144 } 145 146 @Test notGsm_alwaysShowCdmaFalse_usesPrimaryLevelnull147 fun notGsm_alwaysShowCdmaFalse_usesPrimaryLevel() = 148 testScope.runTest { 149 connectionRepository.isGsm.value = false 150 connectionRepository.primaryLevel.value = GSM_LEVEL 151 connectionRepository.cdmaLevel.value = CDMA_LEVEL 152 mobileIconsInteractor.alwaysUseCdmaLevel.value = false 153 154 var latest: Int? = null 155 val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this) 156 157 assertThat(latest).isEqualTo(GSM_LEVEL) 158 159 job.cancel() 160 } 161 162 @Test numberOfLevels_comesFromRepo_whenApplicablenull163 fun numberOfLevels_comesFromRepo_whenApplicable() = 164 testScope.runTest { 165 var latest: Int? = null 166 val job = 167 underTest.signalLevelIcon 168 .onEach { latest = (it as? SignalIconModel.Cellular)?.numberOfLevels } 169 .launchIn(this) 170 171 connectionRepository.numberOfLevels.value = 5 172 assertThat(latest).isEqualTo(5) 173 174 connectionRepository.numberOfLevels.value = 4 175 assertThat(latest).isEqualTo(4) 176 177 job.cancel() 178 } 179 180 @Test inflateSignalStrength_arbitrarilyAddsOneToTheReportedLevelnull181 fun inflateSignalStrength_arbitrarilyAddsOneToTheReportedLevel() = 182 testScope.runTest { 183 connectionRepository.inflateSignalStrength.value = false 184 val latest by collectLastValue(underTest.signalLevelIcon) 185 186 connectionRepository.primaryLevel.value = 4 187 assertThat(latest!!.level).isEqualTo(4) 188 189 connectionRepository.inflateSignalStrength.value = true 190 connectionRepository.primaryLevel.value = 4 191 192 // when INFLATE_SIGNAL_STRENGTH is true, we add 1 to the reported signal level 193 assertThat(latest!!.level).isEqualTo(5) 194 } 195 196 @Test networkSlice_configOn_hasPrioritizedCaps_showsSlicenull197 fun networkSlice_configOn_hasPrioritizedCaps_showsSlice() = 198 testScope.runTest { 199 connectionRepository.allowNetworkSliceIndicator.value = true 200 val latest by collectLastValue(underTest.showSliceAttribution) 201 202 connectionRepository.hasPrioritizedNetworkCapabilities.value = true 203 204 assertThat(latest).isTrue() 205 } 206 207 @Test networkSlice_configOn_noPrioritizedCaps_noSlicenull208 fun networkSlice_configOn_noPrioritizedCaps_noSlice() = 209 testScope.runTest { 210 connectionRepository.allowNetworkSliceIndicator.value = true 211 val latest by collectLastValue(underTest.showSliceAttribution) 212 213 connectionRepository.hasPrioritizedNetworkCapabilities.value = false 214 215 assertThat(latest).isFalse() 216 } 217 218 @Test networkSlice_configOff_hasPrioritizedCaps_noSlicenull219 fun networkSlice_configOff_hasPrioritizedCaps_noSlice() = 220 testScope.runTest { 221 connectionRepository.allowNetworkSliceIndicator.value = false 222 val latest by collectLastValue(underTest.showSliceAttribution) 223 224 connectionRepository.hasPrioritizedNetworkCapabilities.value = true 225 226 assertThat(latest).isFalse() 227 } 228 229 @Test networkSlice_configOff_noPrioritizedCaps_noSlicenull230 fun networkSlice_configOff_noPrioritizedCaps_noSlice() = 231 testScope.runTest { 232 connectionRepository.allowNetworkSliceIndicator.value = false 233 val latest by collectLastValue(underTest.showSliceAttribution) 234 235 connectionRepository.hasPrioritizedNetworkCapabilities.value = false 236 237 assertThat(latest).isFalse() 238 } 239 240 @Test iconGroup_three_gnull241 fun iconGroup_three_g() = 242 testScope.runTest { 243 connectionRepository.resolvedNetworkType.value = 244 DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G)) 245 246 var latest: NetworkTypeIconModel? = null 247 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 248 249 assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)) 250 251 job.cancel() 252 } 253 254 @Test iconGroup_updates_on_changenull255 fun iconGroup_updates_on_change() = 256 testScope.runTest { 257 connectionRepository.resolvedNetworkType.value = 258 DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G)) 259 260 var latest: NetworkTypeIconModel? = null 261 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 262 263 connectionRepository.resolvedNetworkType.value = 264 DefaultNetworkType(mobileMappingsProxy.toIconKey(FOUR_G)) 265 266 assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.FOUR_G)) 267 268 job.cancel() 269 } 270 271 @Test iconGroup_5g_override_typenull272 fun iconGroup_5g_override_type() = 273 testScope.runTest { 274 connectionRepository.resolvedNetworkType.value = 275 OverrideNetworkType(mobileMappingsProxy.toIconKeyOverride(FIVE_G_OVERRIDE)) 276 277 var latest: NetworkTypeIconModel? = null 278 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 279 280 assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.NR_5G)) 281 282 job.cancel() 283 } 284 285 @Test iconGroup_default_if_no_lookupnull286 fun iconGroup_default_if_no_lookup() = 287 testScope.runTest { 288 connectionRepository.resolvedNetworkType.value = 289 DefaultNetworkType(mobileMappingsProxy.toIconKey(NETWORK_TYPE_UNKNOWN)) 290 291 var latest: NetworkTypeIconModel? = null 292 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 293 294 assertThat(latest) 295 .isEqualTo(NetworkTypeIconModel.DefaultIcon(FakeMobileIconsInteractor.DEFAULT_ICON)) 296 297 job.cancel() 298 } 299 300 @Test iconGroup_carrierMerged_usesOverridenull301 fun iconGroup_carrierMerged_usesOverride() = 302 testScope.runTest { 303 connectionRepository.resolvedNetworkType.value = CarrierMergedNetworkType 304 305 var latest: NetworkTypeIconModel? = null 306 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 307 308 assertThat(latest) 309 .isEqualTo( 310 NetworkTypeIconModel.DefaultIcon(CarrierMergedNetworkType.iconGroupOverride) 311 ) 312 313 job.cancel() 314 } 315 316 @Test overrideIcon_usesCarrierIdOverridenull317 fun overrideIcon_usesCarrierIdOverride() = 318 testScope.runTest { 319 val overrides = 320 mock<MobileIconCarrierIdOverrides>().also { 321 whenever(it.carrierIdEntryExists(anyInt())).thenReturn(true) 322 whenever(it.getOverrideFor(anyInt(), anyString(), any())).thenReturn(1234) 323 } 324 325 underTest = createInteractor(overrides) 326 327 connectionRepository.resolvedNetworkType.value = 328 DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G)) 329 330 var latest: NetworkTypeIconModel? = null 331 val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) 332 333 assertThat(latest) 334 .isEqualTo(NetworkTypeIconModel.OverriddenIcon(TelephonyIcons.THREE_G, 1234)) 335 336 job.cancel() 337 } 338 339 @Test alwaysShowDataRatIcon_matchesParentnull340 fun alwaysShowDataRatIcon_matchesParent() = 341 testScope.runTest { 342 var latest: Boolean? = null 343 val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this) 344 345 mobileIconsInteractor.alwaysShowDataRatIcon.value = true 346 assertThat(latest).isTrue() 347 348 mobileIconsInteractor.alwaysShowDataRatIcon.value = false 349 assertThat(latest).isFalse() 350 351 job.cancel() 352 } 353 354 @Test dataState_connectednull355 fun dataState_connected() = 356 testScope.runTest { 357 var latest: Boolean? = null 358 val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this) 359 360 connectionRepository.dataConnectionState.value = DataConnectionState.Connected 361 362 assertThat(latest).isTrue() 363 364 job.cancel() 365 } 366 367 @Test dataState_notConnectednull368 fun dataState_notConnected() = 369 testScope.runTest { 370 var latest: Boolean? = null 371 val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this) 372 373 connectionRepository.dataConnectionState.value = DataConnectionState.Disconnected 374 375 assertThat(latest).isFalse() 376 377 job.cancel() 378 } 379 380 @Test isInService_usesRepositoryValuenull381 fun isInService_usesRepositoryValue() = 382 testScope.runTest { 383 var latest: Boolean? = null 384 val job = underTest.isInService.onEach { latest = it }.launchIn(this) 385 386 connectionRepository.isInService.value = true 387 388 assertThat(latest).isTrue() 389 390 connectionRepository.isInService.value = false 391 392 assertThat(latest).isFalse() 393 394 job.cancel() 395 } 396 397 @Test roaming_isGsm_usesConnectionModelnull398 fun roaming_isGsm_usesConnectionModel() = 399 testScope.runTest { 400 var latest: Boolean? = null 401 val job = underTest.isRoaming.onEach { latest = it }.launchIn(this) 402 403 connectionRepository.cdmaRoaming.value = true 404 connectionRepository.isGsm.value = true 405 connectionRepository.isRoaming.value = false 406 407 assertThat(latest).isFalse() 408 409 connectionRepository.isRoaming.value = true 410 411 assertThat(latest).isTrue() 412 413 job.cancel() 414 } 415 416 @Test roaming_isCdma_usesCdmaRoamingBitnull417 fun roaming_isCdma_usesCdmaRoamingBit() = 418 testScope.runTest { 419 var latest: Boolean? = null 420 val job = underTest.isRoaming.onEach { latest = it }.launchIn(this) 421 422 connectionRepository.cdmaRoaming.value = false 423 connectionRepository.isGsm.value = false 424 connectionRepository.isRoaming.value = true 425 426 assertThat(latest).isFalse() 427 428 connectionRepository.cdmaRoaming.value = true 429 connectionRepository.isGsm.value = false 430 connectionRepository.isRoaming.value = false 431 432 assertThat(latest).isTrue() 433 434 job.cancel() 435 } 436 437 @Test roaming_falseWhileCarrierNetworkChangeActivenull438 fun roaming_falseWhileCarrierNetworkChangeActive() = 439 testScope.runTest { 440 var latest: Boolean? = null 441 val job = underTest.isRoaming.onEach { latest = it }.launchIn(this) 442 443 connectionRepository.cdmaRoaming.value = true 444 connectionRepository.isGsm.value = false 445 connectionRepository.isRoaming.value = true 446 connectionRepository.carrierNetworkChangeActive.value = true 447 448 assertThat(latest).isFalse() 449 450 connectionRepository.cdmaRoaming.value = true 451 connectionRepository.isGsm.value = true 452 453 assertThat(latest).isFalse() 454 455 job.cancel() 456 } 457 458 @Test networkName_usesOperatorAlphaShortWhenNonNullAndRepoIsDefaultnull459 fun networkName_usesOperatorAlphaShortWhenNonNullAndRepoIsDefault() = 460 testScope.runTest { 461 var latest: NetworkNameModel? = null 462 val job = underTest.networkName.onEach { latest = it }.launchIn(this) 463 464 val testOperatorName = "operatorAlphaShort" 465 466 // Default network name, operator name is non-null, uses the operator name 467 connectionRepository.networkName.value = DEFAULT_NAME_MODEL 468 connectionRepository.operatorAlphaShort.value = testOperatorName 469 470 assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived(testOperatorName)) 471 472 // Default network name, operator name is null, uses the default 473 connectionRepository.operatorAlphaShort.value = null 474 475 assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL) 476 477 // Derived network name, operator name non-null, uses the derived name 478 connectionRepository.networkName.value = DERIVED_NAME_MODEL 479 connectionRepository.operatorAlphaShort.value = testOperatorName 480 481 assertThat(latest).isEqualTo(DERIVED_NAME_MODEL) 482 483 job.cancel() 484 } 485 486 @Test networkNameForSubId_usesOperatorAlphaShortWhenNonNullAndRepoIsDefaultnull487 fun networkNameForSubId_usesOperatorAlphaShortWhenNonNullAndRepoIsDefault() = 488 testScope.runTest { 489 var latest: String? = null 490 val job = underTest.carrierName.onEach { latest = it }.launchIn(this) 491 492 val testOperatorName = "operatorAlphaShort" 493 494 // Default network name, operator name is non-null, uses the operator name 495 connectionRepository.carrierName.value = DEFAULT_NAME_MODEL 496 connectionRepository.operatorAlphaShort.value = testOperatorName 497 498 assertThat(latest).isEqualTo(testOperatorName) 499 500 // Default network name, operator name is null, uses the default 501 connectionRepository.operatorAlphaShort.value = null 502 503 assertThat(latest).isEqualTo(DEFAULT_NAME) 504 505 // Derived network name, operator name non-null, uses the derived name 506 connectionRepository.carrierName.value = 507 NetworkNameModel.SubscriptionDerived(DERIVED_NAME) 508 connectionRepository.operatorAlphaShort.value = testOperatorName 509 510 assertThat(latest).isEqualTo(DERIVED_NAME) 511 512 job.cancel() 513 } 514 515 @Test isSingleCarrier_matchesParentnull516 fun isSingleCarrier_matchesParent() = 517 testScope.runTest { 518 var latest: Boolean? = null 519 val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this) 520 521 mobileIconsInteractor.isSingleCarrier.value = true 522 assertThat(latest).isTrue() 523 524 mobileIconsInteractor.isSingleCarrier.value = false 525 assertThat(latest).isFalse() 526 527 job.cancel() 528 } 529 530 @Test isForceHidden_matchesParentnull531 fun isForceHidden_matchesParent() = 532 testScope.runTest { 533 var latest: Boolean? = null 534 val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this) 535 536 mobileIconsInteractor.isForceHidden.value = true 537 assertThat(latest).isTrue() 538 539 mobileIconsInteractor.isForceHidden.value = false 540 assertThat(latest).isFalse() 541 542 job.cancel() 543 } 544 545 @Test isAllowedDuringAirplaneMode_matchesReponull546 fun isAllowedDuringAirplaneMode_matchesRepo() = 547 testScope.runTest { 548 val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode) 549 550 connectionRepository.isAllowedDuringAirplaneMode.value = true 551 assertThat(latest).isTrue() 552 553 connectionRepository.isAllowedDuringAirplaneMode.value = false 554 assertThat(latest).isFalse() 555 } 556 557 @Test cellBasedIconId_correctLevel_notCutoutnull558 fun cellBasedIconId_correctLevel_notCutout() = 559 testScope.runTest { 560 connectionRepository.isNonTerrestrial.value = false 561 connectionRepository.isInService.value = true 562 connectionRepository.primaryLevel.value = 1 563 connectionRepository.setDataEnabled(false) 564 connectionRepository.isNonTerrestrial.value = false 565 566 var latest: SignalIconModel.Cellular? = null 567 val job = 568 underTest.signalLevelIcon 569 .onEach { latest = it as? SignalIconModel.Cellular } 570 .launchIn(this) 571 572 assertThat(latest?.level).isEqualTo(1) 573 assertThat(latest?.showExclamationMark).isFalse() 574 575 job.cancel() 576 } 577 578 @Test icon_usesLevelFromInteractornull579 fun icon_usesLevelFromInteractor() = 580 testScope.runTest { 581 connectionRepository.isNonTerrestrial.value = false 582 connectionRepository.isInService.value = true 583 584 var latest: SignalIconModel? = null 585 val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this) 586 587 connectionRepository.primaryLevel.value = 3 588 assertThat(latest!!.level).isEqualTo(3) 589 590 connectionRepository.primaryLevel.value = 1 591 assertThat(latest!!.level).isEqualTo(1) 592 593 job.cancel() 594 } 595 596 @Test cellBasedIcon_usesNumberOfLevelsFromInteractornull597 fun cellBasedIcon_usesNumberOfLevelsFromInteractor() = 598 testScope.runTest { 599 connectionRepository.isNonTerrestrial.value = false 600 601 var latest: SignalIconModel.Cellular? = null 602 val job = 603 underTest.signalLevelIcon 604 .onEach { latest = it as? SignalIconModel.Cellular } 605 .launchIn(this) 606 607 connectionRepository.numberOfLevels.value = 5 608 assertThat(latest!!.numberOfLevels).isEqualTo(5) 609 610 connectionRepository.numberOfLevels.value = 2 611 assertThat(latest!!.numberOfLevels).isEqualTo(2) 612 613 job.cancel() 614 } 615 616 @Test cellBasedIcon_defaultDataDisabled_showExclamationTruenull617 fun cellBasedIcon_defaultDataDisabled_showExclamationTrue() = 618 testScope.runTest { 619 connectionRepository.isNonTerrestrial.value = false 620 mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = false 621 622 var latest: SignalIconModel.Cellular? = null 623 val job = 624 underTest.signalLevelIcon 625 .onEach { latest = it as? SignalIconModel.Cellular } 626 .launchIn(this) 627 628 assertThat(latest!!.showExclamationMark).isTrue() 629 630 job.cancel() 631 } 632 633 @Test cellBasedIcon_defaultConnectionFailed_showExclamationTruenull634 fun cellBasedIcon_defaultConnectionFailed_showExclamationTrue() = 635 testScope.runTest { 636 connectionRepository.isNonTerrestrial.value = false 637 mobileIconsInteractor.isDefaultConnectionFailed.value = true 638 639 var latest: SignalIconModel.Cellular? = null 640 val job = 641 underTest.signalLevelIcon 642 .onEach { latest = it as? SignalIconModel.Cellular } 643 .launchIn(this) 644 645 assertThat(latest!!.showExclamationMark).isTrue() 646 647 job.cancel() 648 } 649 650 @Test cellBasedIcon_enabledAndNotFailed_showExclamationFalsenull651 fun cellBasedIcon_enabledAndNotFailed_showExclamationFalse() = 652 testScope.runTest { 653 connectionRepository.isNonTerrestrial.value = false 654 connectionRepository.isInService.value = true 655 mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true 656 mobileIconsInteractor.isDefaultConnectionFailed.value = false 657 658 var latest: SignalIconModel.Cellular? = null 659 val job = 660 underTest.signalLevelIcon 661 .onEach { latest = it as? SignalIconModel.Cellular } 662 .launchIn(this) 663 664 assertThat(latest!!.showExclamationMark).isFalse() 665 666 job.cancel() 667 } 668 669 @Test cellBasedIcon_usesEmptyState_whenNotInServicenull670 fun cellBasedIcon_usesEmptyState_whenNotInService() = 671 testScope.runTest { 672 var latest: SignalIconModel.Cellular? = null 673 val job = 674 underTest.signalLevelIcon 675 .onEach { latest = it as? SignalIconModel.Cellular } 676 .launchIn(this) 677 678 connectionRepository.isNonTerrestrial.value = false 679 connectionRepository.isInService.value = false 680 681 assertThat(latest?.level).isEqualTo(0) 682 assertThat(latest?.showExclamationMark).isTrue() 683 684 // Changing the level doesn't overwrite the disabled state 685 connectionRepository.primaryLevel.value = 2 686 assertThat(latest?.level).isEqualTo(0) 687 assertThat(latest?.showExclamationMark).isTrue() 688 689 // Once back in service, the regular icon appears 690 connectionRepository.isInService.value = true 691 assertThat(latest?.level).isEqualTo(2) 692 assertThat(latest?.showExclamationMark).isFalse() 693 694 job.cancel() 695 } 696 697 @Test cellBasedIcon_usesCarrierNetworkState_whenInCarrierNetworkChangeModenull698 fun cellBasedIcon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() = 699 testScope.runTest { 700 var latest: SignalIconModel.Cellular? = null 701 val job = 702 underTest.signalLevelIcon 703 .onEach { latest = it as? SignalIconModel.Cellular? } 704 .launchIn(this) 705 706 connectionRepository.isNonTerrestrial.value = false 707 connectionRepository.isInService.value = true 708 connectionRepository.carrierNetworkChangeActive.value = true 709 connectionRepository.primaryLevel.value = 1 710 connectionRepository.cdmaLevel.value = 1 711 712 assertThat(latest!!.level).isEqualTo(1) 713 assertThat(latest!!.carrierNetworkChange).isTrue() 714 715 // SignalIconModel respects the current level 716 connectionRepository.primaryLevel.value = 2 717 718 assertThat(latest!!.level).isEqualTo(2) 719 assertThat(latest!!.carrierNetworkChange).isTrue() 720 721 job.cancel() 722 } 723 724 @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) 725 @Test satBasedIcon_isUsedWhenNonTerrestrialnull726 fun satBasedIcon_isUsedWhenNonTerrestrial() = 727 testScope.runTest { 728 val latest by collectLastValue(underTest.signalLevelIcon) 729 730 // Start off using cellular 731 assertThat(latest).isInstanceOf(SignalIconModel.Cellular::class.java) 732 733 connectionRepository.isNonTerrestrial.value = true 734 735 assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java) 736 } 737 createInteractornull738 private fun createInteractor( 739 overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl() 740 ) = 741 MobileIconInteractorImpl( 742 testScope.backgroundScope, 743 mobileIconsInteractor.activeDataConnectionHasDataEnabled, 744 mobileIconsInteractor.alwaysShowDataRatIcon, 745 mobileIconsInteractor.alwaysUseCdmaLevel, 746 mobileIconsInteractor.isSingleCarrier, 747 mobileIconsInteractor.mobileIsDefault, 748 mobileIconsInteractor.defaultMobileIconMapping, 749 mobileIconsInteractor.defaultMobileIconGroup, 750 mobileIconsInteractor.isDefaultConnectionFailed, 751 mobileIconsInteractor.isForceHidden, 752 connectionRepository, 753 context, 754 overrides, 755 ) 756 757 companion object { 758 private const val GSM_LEVEL = 1 759 private const val CDMA_LEVEL = 2 760 761 private const val SUB_1_ID = 1 762 763 private const val DEFAULT_NAME = "test default name" 764 private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME) 765 private const val DERIVED_NAME = "test derived name" 766 private val DERIVED_NAME_MODEL = NetworkNameModel.IntentDerived(DERIVED_NAME) 767 } 768 } 769