1 /* <lambda>null2 * Copyright (C) 2020 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.controls.controller 18 19 import android.app.PendingIntent 20 import android.content.ComponentName 21 import android.content.Context 22 import android.content.ContextWrapper 23 import android.content.Intent 24 import android.content.pm.ServiceInfo 25 import android.os.UserHandle 26 import android.service.controls.Control 27 import android.service.controls.DeviceTypes 28 import android.service.controls.actions.ControlAction 29 import androidx.test.ext.junit.runners.AndroidJUnit4 30 import androidx.test.filters.SmallTest 31 import com.android.systemui.SysuiTestCase 32 import com.android.systemui.backup.BackupHelper 33 import com.android.systemui.controls.ControlStatus 34 import com.android.systemui.controls.ControlsServiceInfo 35 import com.android.systemui.controls.management.ControlsListingController 36 import com.android.systemui.controls.panels.AuthorizedPanelsRepository 37 import com.android.systemui.controls.panels.selectedComponentRepository 38 import com.android.systemui.controls.ui.ControlsUiController 39 import com.android.systemui.dump.DumpManager 40 import com.android.systemui.settings.UserFileManager 41 import com.android.systemui.settings.UserTracker 42 import com.android.systemui.testKosmos 43 import com.android.systemui.util.concurrency.FakeExecutor 44 import com.android.systemui.util.mockito.whenever 45 import com.android.systemui.util.time.FakeSystemClock 46 import com.google.common.truth.Truth.assertThat 47 import org.junit.After 48 import org.junit.Assert.assertEquals 49 import org.junit.Assert.assertFalse 50 import org.junit.Assert.assertNotEquals 51 import org.junit.Assert.assertTrue 52 import org.junit.Before 53 import org.junit.Test 54 import org.junit.runner.RunWith 55 import org.mockito.ArgumentCaptor 56 import org.mockito.ArgumentMatchers 57 import org.mockito.ArgumentMatchers.anyString 58 import org.mockito.Captor 59 import org.mockito.Mock 60 import org.mockito.Mockito 61 import org.mockito.Mockito.anyInt 62 import org.mockito.Mockito.clearInvocations 63 import org.mockito.Mockito.inOrder 64 import org.mockito.Mockito.mock 65 import org.mockito.Mockito.never 66 import org.mockito.Mockito.reset 67 import org.mockito.Mockito.times 68 import org.mockito.Mockito.verify 69 import org.mockito.Mockito.verifyNoMoreInteractions 70 import org.mockito.Mockito.`when` 71 import org.mockito.MockitoAnnotations 72 import java.io.File 73 import java.util.Optional 74 import java.util.function.Consumer 75 76 @SmallTest 77 @RunWith(AndroidJUnit4::class) 78 class ControlsControllerImplTest : SysuiTestCase() { 79 private val kosmos = testKosmos() 80 81 @Mock 82 private lateinit var uiController: ControlsUiController 83 @Mock 84 private lateinit var bindingController: ControlsBindingController 85 @Mock 86 private lateinit var pendingIntent: PendingIntent 87 @Mock 88 private lateinit var persistenceWrapper: ControlsFavoritePersistenceWrapper 89 @Mock 90 private lateinit var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper 91 @Mock 92 private lateinit var listingController: ControlsListingController 93 @Mock 94 private lateinit var userTracker: UserTracker 95 @Mock 96 private lateinit var userFileManager: UserFileManager 97 @Mock 98 private lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository 99 100 @Captor 101 private lateinit var structureInfoCaptor: ArgumentCaptor<StructureInfo> 102 103 @Captor 104 private lateinit var controlLoadCallbackCaptor: 105 ArgumentCaptor<ControlsBindingController.LoadCallback> 106 @Captor 107 private lateinit var controlLoadCallbackCaptor2: 108 ArgumentCaptor<ControlsBindingController.LoadCallback> 109 110 @Captor 111 private lateinit var listingCallbackCaptor: 112 ArgumentCaptor<ControlsListingController.ControlsListingCallback> 113 114 private lateinit var delayableExecutor: FakeExecutor 115 private lateinit var controller: ControlsControllerImpl 116 private lateinit var canceller: DidRunRunnable 117 118 companion object { 119 fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() 120 fun <T> eq(value: T): T = Mockito.eq(value) ?: value 121 fun <T> any(): T = Mockito.any<T>() 122 123 private val TEST_COMPONENT = ComponentName("test.pkg", "test.class") 124 private const val TEST_CONTROL_ID = "control1" 125 private const val TEST_CONTROL_TITLE = "Test" 126 private const val TEST_CONTROL_SUBTITLE = "TestSub" 127 private const val TEST_DEVICE_TYPE = DeviceTypes.TYPE_AC_HEATER 128 private const val TEST_STRUCTURE = "" 129 private val TEST_CONTROL_INFO = ControlInfo(TEST_CONTROL_ID, 130 TEST_CONTROL_TITLE, TEST_CONTROL_SUBTITLE, TEST_DEVICE_TYPE) 131 private val TEST_STRUCTURE_INFO = StructureInfo(TEST_COMPONENT, 132 TEST_STRUCTURE, listOf(TEST_CONTROL_INFO)) 133 134 private val TEST_COMPONENT_2 = ComponentName("test.pkg", "test.class.2") 135 private const val TEST_CONTROL_ID_2 = "control2" 136 private const val TEST_CONTROL_TITLE_2 = "Test 2" 137 private const val TEST_CONTROL_SUBTITLE_2 = "TestSub 2" 138 private const val TEST_DEVICE_TYPE_2 = DeviceTypes.TYPE_CAMERA 139 private const val TEST_STRUCTURE_2 = "My House" 140 private val TEST_CONTROL_INFO_2 = ControlInfo(TEST_CONTROL_ID_2, 141 TEST_CONTROL_TITLE_2, TEST_CONTROL_SUBTITLE_2, TEST_DEVICE_TYPE_2) 142 private val TEST_STRUCTURE_INFO_2 = StructureInfo(TEST_COMPONENT_2, 143 TEST_STRUCTURE_2, listOf(TEST_CONTROL_INFO_2)) 144 } 145 146 private val user = mContext.userId 147 private val otherUser = user + 1 148 149 @Before 150 fun setUp() { 151 MockitoAnnotations.initMocks(this) 152 153 whenever(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf()) 154 `when`(userTracker.userHandle).thenReturn(UserHandle.of(user)) 155 156 delayableExecutor = FakeExecutor(FakeSystemClock()) 157 158 val wrapper = object : ContextWrapper(mContext) { 159 override fun createContextAsUser(user: UserHandle, flags: Int): Context { 160 return baseContext 161 } 162 } 163 164 canceller = DidRunRunnable() 165 `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) 166 `when`(userFileManager.getFile(anyString(), anyInt())).thenReturn(mock(File::class.java)) 167 `when`(userFileManager.getSharedPreferences(anyString(), anyInt(), anyInt())) 168 .thenReturn(context.getSharedPreferences("test", Context.MODE_PRIVATE)) 169 170 controller = ControlsControllerImpl( 171 wrapper, 172 delayableExecutor, 173 uiController, 174 kosmos.selectedComponentRepository, 175 bindingController, 176 listingController, 177 userFileManager, 178 userTracker, 179 authorizedPanelsRepository, 180 Optional.of(persistenceWrapper), 181 mock(DumpManager::class.java) 182 ) 183 controller.auxiliaryPersistenceWrapper = auxiliaryPersistenceWrapper 184 185 verify(listingController).addCallback(capture(listingCallbackCaptor)) 186 } 187 188 @After 189 fun tearDown() { 190 controller.destroy() 191 } 192 193 private fun statelessBuilderFromInfo( 194 controlInfo: ControlInfo, 195 structure: CharSequence = "" 196 ): Control.StatelessBuilder { 197 return Control.StatelessBuilder(controlInfo.controlId, pendingIntent) 198 .setDeviceType(controlInfo.deviceType).setTitle(controlInfo.controlTitle) 199 .setSubtitle(controlInfo.controlSubtitle).setStructure(structure) 200 } 201 202 private fun statefulBuilderFromInfo( 203 controlInfo: ControlInfo, 204 structure: CharSequence = "" 205 ): Control.StatefulBuilder { 206 return Control.StatefulBuilder(controlInfo.controlId, pendingIntent) 207 .setDeviceType(controlInfo.deviceType).setTitle(controlInfo.controlTitle) 208 .setSubtitle(controlInfo.controlSubtitle).setStructure(structure) 209 } 210 211 @Test 212 fun testStartOnUser() { 213 assertEquals(user, controller.currentUserId) 214 } 215 216 @Test 217 fun testStartWithoutFavorites() { 218 assertTrue(controller.getFavorites().isEmpty()) 219 } 220 221 @Test 222 fun testStartWithSavedFavorites() { 223 `when`(persistenceWrapper.readFavorites()).thenReturn(listOf(TEST_STRUCTURE_INFO)) 224 val controller_other = ControlsControllerImpl( 225 mContext, 226 delayableExecutor, 227 uiController, 228 kosmos.selectedComponentRepository, 229 bindingController, 230 listingController, 231 userFileManager, 232 userTracker, 233 authorizedPanelsRepository, 234 Optional.of(persistenceWrapper), 235 mock(DumpManager::class.java) 236 ) 237 assertEquals(listOf(TEST_STRUCTURE_INFO), controller_other.getFavorites()) 238 } 239 240 @Test 241 fun testAddAuthorizedPackagesFromSavedFavoritesOnStart() { 242 clearInvocations(authorizedPanelsRepository) 243 `when`(persistenceWrapper.readFavorites()).thenReturn(listOf(TEST_STRUCTURE_INFO)) 244 ControlsControllerImpl( 245 mContext, 246 delayableExecutor, 247 uiController, 248 kosmos.selectedComponentRepository, 249 bindingController, 250 listingController, 251 userFileManager, 252 userTracker, 253 authorizedPanelsRepository, 254 Optional.of(persistenceWrapper), 255 mock(DumpManager::class.java) 256 ) 257 verify(authorizedPanelsRepository) 258 .addAuthorizedPanels(setOf(TEST_STRUCTURE_INFO.componentName.packageName)) 259 } 260 261 @Test 262 fun testOnActionResponse() { 263 controller.onActionResponse(TEST_COMPONENT, TEST_CONTROL_ID, ControlAction.RESPONSE_OK) 264 265 verify(uiController).onActionResponse(TEST_COMPONENT, TEST_CONTROL_ID, 266 ControlAction.RESPONSE_OK) 267 } 268 269 @Test 270 fun testRefreshStatus() { 271 val control = Control.StatefulBuilder(TEST_CONTROL_ID, pendingIntent).build() 272 val list = listOf(control) 273 controller.refreshStatus(TEST_COMPONENT, control) 274 275 verify(uiController).onRefreshState(TEST_COMPONENT, list) 276 } 277 278 @Test 279 fun testUnsubscribe() { 280 controller.unsubscribe() 281 verify(bindingController).unsubscribe() 282 } 283 284 @Test 285 fun testSubscribeFavorites() { 286 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 287 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2) 288 delayableExecutor.runAllReady() 289 290 controller.subscribeToFavorites(TEST_STRUCTURE_INFO) 291 292 verify(bindingController).subscribe(capture(structureInfoCaptor)) 293 294 assertTrue(TEST_CONTROL_INFO in structureInfoCaptor.value.controls) 295 assertFalse(TEST_CONTROL_INFO_2 in structureInfoCaptor.value.controls) 296 } 297 298 @Test 299 fun testLoadForComponent_noFavorites() { 300 var loaded = false 301 val control = statelessBuilderFromInfo(TEST_CONTROL_INFO).build() 302 303 controller.loadForComponent(TEST_COMPONENT, Consumer { data -> 304 val controls = data.allControls 305 val favorites = data.favoritesIds 306 loaded = true 307 assertEquals(1, controls.size) 308 val controlStatus = controls[0] 309 assertEquals(ControlStatus(control, TEST_COMPONENT, false), controlStatus) 310 311 assertTrue(favorites.isEmpty()) 312 assertFalse(data.errorOnLoad) 313 }, Consumer {}) 314 315 verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), 316 capture(controlLoadCallbackCaptor)) 317 318 controlLoadCallbackCaptor.value.accept(listOf(control)) 319 320 delayableExecutor.runAllReady() 321 322 assertTrue(loaded) 323 } 324 325 @Test 326 fun testLoadForComponent_favorites() { 327 var loaded = false 328 val control = statelessBuilderFromInfo(TEST_CONTROL_INFO).build() 329 val control2 = statelessBuilderFromInfo(TEST_CONTROL_INFO_2).build() 330 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 331 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2) 332 delayableExecutor.runAllReady() 333 334 controller.loadForComponent(TEST_COMPONENT, Consumer { data -> 335 val controls = data.allControls 336 val favorites = data.favoritesIds 337 loaded = true 338 assertEquals(2, controls.size) 339 val controlStatus = controls.first { it.control.controlId == TEST_CONTROL_ID } 340 assertEquals(ControlStatus(control, TEST_COMPONENT, true), controlStatus) 341 342 val controlStatus2 = controls.first { it.control.controlId == TEST_CONTROL_ID_2 } 343 assertEquals(ControlStatus(control2, TEST_COMPONENT, false), controlStatus2) 344 345 assertEquals(1, favorites.size) 346 assertEquals(TEST_CONTROL_ID, favorites[0]) 347 assertFalse(data.errorOnLoad) 348 }, Consumer {}) 349 350 verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), 351 capture(controlLoadCallbackCaptor)) 352 353 controlLoadCallbackCaptor.value.accept(listOf(control, control2)) 354 delayableExecutor.runAllReady() 355 356 assertTrue(loaded) 357 } 358 359 @Test 360 fun testLoadForComponent_removed() { 361 var loaded = false 362 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 363 delayableExecutor.runAllReady() 364 365 controller.loadForComponent(TEST_COMPONENT, Consumer { data -> 366 val controls = data.allControls 367 val favorites = data.favoritesIds 368 loaded = true 369 assertEquals(1, controls.size) 370 val controlStatus = controls[0] 371 assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId) 372 assertEquals(TEST_STRUCTURE_INFO.structure, controlStatus.control.structure) 373 assertTrue(controlStatus.favorite) 374 assertTrue(controlStatus.removed) 375 376 assertEquals(1, favorites.size) 377 assertEquals(TEST_CONTROL_ID, favorites[0]) 378 assertFalse(data.errorOnLoad) 379 }, Consumer {}) 380 381 verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), 382 capture(controlLoadCallbackCaptor)) 383 384 controlLoadCallbackCaptor.value.accept(emptyList()) 385 delayableExecutor.runAllReady() 386 387 assertTrue(loaded) 388 } 389 390 @Test 391 fun testErrorOnLoad_notRemoved() { 392 var loaded = false 393 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 394 delayableExecutor.runAllReady() 395 396 controller.loadForComponent(TEST_COMPONENT, Consumer { data -> 397 val controls = data.allControls 398 val favorites = data.favoritesIds 399 loaded = true 400 assertEquals(1, controls.size) 401 val controlStatus = controls[0] 402 assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId) 403 assertEquals(TEST_STRUCTURE_INFO.structure, controlStatus.control.structure) 404 assertTrue(controlStatus.favorite) 405 assertFalse(controlStatus.removed) 406 407 assertEquals(1, favorites.size) 408 assertEquals(TEST_CONTROL_ID, favorites[0]) 409 assertTrue(data.errorOnLoad) 410 }, Consumer {}) 411 412 verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), 413 capture(controlLoadCallbackCaptor)) 414 415 controlLoadCallbackCaptor.value.error("") 416 417 delayableExecutor.runAllReady() 418 419 assertTrue(loaded) 420 } 421 422 @Test 423 fun testCancelLoad() { 424 var loaded = false 425 var cancelRunnable: Runnable? = null 426 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 427 delayableExecutor.runAllReady() 428 controller.loadForComponent(TEST_COMPONENT, Consumer { 429 loaded = true 430 }, Consumer { runnable -> cancelRunnable = runnable }) 431 432 cancelRunnable?.run() 433 delayableExecutor.runAllReady() 434 435 assertFalse(loaded) 436 assertTrue(canceller.ran) 437 } 438 439 @Test 440 fun testCancelLoad_afterSuccessfulLoad() { 441 var loaded = false 442 var cancelRunnable: Runnable? = null 443 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 444 delayableExecutor.runAllReady() 445 controller.loadForComponent(TEST_COMPONENT, Consumer { 446 loaded = true 447 }, Consumer { runnable -> cancelRunnable = runnable }) 448 449 verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), 450 capture(controlLoadCallbackCaptor)) 451 452 controlLoadCallbackCaptor.value.accept(emptyList()) 453 454 cancelRunnable?.run() 455 delayableExecutor.runAllReady() 456 457 assertTrue(loaded) 458 assertTrue(canceller.ran) 459 } 460 461 @Test 462 fun testCancelLoad_afterErrorLoad() { 463 var loaded = false 464 var cancelRunnable: Runnable? = null 465 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 466 delayableExecutor.runAllReady() 467 controller.loadForComponent(TEST_COMPONENT, Consumer { 468 loaded = true 469 }, Consumer { runnable -> cancelRunnable = runnable }) 470 471 verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), 472 capture(controlLoadCallbackCaptor)) 473 474 controlLoadCallbackCaptor.value.error("") 475 476 cancelRunnable?.run() 477 delayableExecutor.runAllReady() 478 479 assertTrue(loaded) 480 assertTrue(canceller.ran) 481 } 482 483 @Test 484 fun testFavoriteInformationModifiedOnLoad() { 485 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 486 delayableExecutor.runAllReady() 487 488 val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2) 489 val control = statelessBuilderFromInfo(newControlInfo).build() 490 491 controller.loadForComponent(TEST_COMPONENT, Consumer {}, Consumer {}) 492 493 verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), 494 capture(controlLoadCallbackCaptor)) 495 496 controlLoadCallbackCaptor.value.accept(listOf(control)) 497 delayableExecutor.runAllReady() 498 499 val favorites = controller.getFavorites().flatMap { it.controls } 500 assertEquals(1, favorites.size) 501 assertEquals(newControlInfo, favorites[0]) 502 } 503 504 @Test 505 fun testFavoriteInformationModifiedOnRefreshWithOkStatus() { 506 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 507 508 val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2) 509 val control = statefulBuilderFromInfo(newControlInfo).setStatus(Control.STATUS_OK).build() 510 511 controller.refreshStatus(TEST_COMPONENT, control) 512 513 delayableExecutor.runAllReady() 514 515 val favorites = controller.getFavorites().flatMap { it.controls } 516 assertEquals(1, favorites.size) 517 assertEquals(newControlInfo, favorites[0]) 518 } 519 520 @Test 521 fun testFavoriteInformationNotModifiedOnRefreshWithNonOkStatus() { 522 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 523 524 val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2) 525 val control = statefulBuilderFromInfo(newControlInfo).setStatus(Control.STATUS_ERROR) 526 .build() 527 528 controller.refreshStatus(TEST_COMPONENT, control) 529 530 delayableExecutor.runAllReady() 531 532 val favorites = controller.getFavorites().flatMap { it.controls } 533 assertEquals(1, favorites.size) 534 assertEquals(TEST_CONTROL_INFO, favorites[0]) 535 } 536 537 @Test 538 fun testSwitchUsers() { 539 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 540 delayableExecutor.runAllReady() 541 542 reset(persistenceWrapper) 543 544 controller.changeUser(UserHandle.of(otherUser)) 545 546 verify(persistenceWrapper).changeFileAndBackupManager(any(), any()) 547 verify(persistenceWrapper).readFavorites() 548 verify(auxiliaryPersistenceWrapper).changeFile(any()) 549 verify(bindingController).changeUser(UserHandle.of(otherUser)) 550 verify(listingController).changeUser(UserHandle.of(otherUser)) 551 assertTrue(controller.getFavorites().isEmpty()) 552 assertEquals(otherUser, controller.currentUserId) 553 } 554 555 @Test 556 fun testCountFavoritesForComponent_singleComponent() { 557 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 558 delayableExecutor.runAllReady() 559 560 assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT)) 561 assertEquals(0, controller.countFavoritesForComponent(TEST_COMPONENT_2)) 562 } 563 564 @Test 565 fun testCountFavoritesForComponent_multipleComponents() { 566 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 567 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2) 568 delayableExecutor.runAllReady() 569 570 assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT)) 571 assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT_2)) 572 } 573 574 @Test 575 fun testGetFavoritesForComponent() { 576 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 577 delayableExecutor.runAllReady() 578 579 assertEquals(listOf(TEST_STRUCTURE_INFO), 580 controller.getFavoritesForComponent(TEST_COMPONENT)) 581 } 582 583 @Test 584 fun testGetFavoritesForComponent_otherComponent() { 585 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2) 586 delayableExecutor.runAllReady() 587 588 assertTrue(controller.getFavoritesForComponent(TEST_COMPONENT).isEmpty()) 589 } 590 591 @Test 592 fun testGetFavoritesForComponent_multipleInOrder() { 593 val controlInfo = ControlInfo("id", "title", "subtitle", 0) 594 595 controller.replaceFavoritesForStructure( 596 StructureInfo( 597 TEST_COMPONENT, 598 "Home", 599 listOf(TEST_CONTROL_INFO, controlInfo) 600 )) 601 delayableExecutor.runAllReady() 602 603 assertEquals(listOf(TEST_CONTROL_INFO, controlInfo), 604 controller.getFavoritesForComponent(TEST_COMPONENT).flatMap { it.controls }) 605 606 controller.replaceFavoritesForStructure( 607 StructureInfo( 608 TEST_COMPONENT, 609 "Home", 610 listOf(controlInfo, TEST_CONTROL_INFO) 611 )) 612 delayableExecutor.runAllReady() 613 614 assertEquals(listOf(controlInfo, TEST_CONTROL_INFO), 615 controller.getFavoritesForComponent(TEST_COMPONENT).flatMap { it.controls }) 616 } 617 618 @Test 619 fun testReplaceFavoritesForStructure_noExistingFavorites() { 620 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 621 delayableExecutor.runAllReady() 622 623 assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT)) 624 assertEquals(listOf(TEST_STRUCTURE_INFO), 625 controller.getFavoritesForComponent(TEST_COMPONENT)) 626 } 627 628 @Test 629 fun testReplaceFavoritesForStructure_doNotStoreEmptyStructure() { 630 controller.replaceFavoritesForStructure( 631 StructureInfo(TEST_COMPONENT, "Home", emptyList<ControlInfo>())) 632 delayableExecutor.runAllReady() 633 634 assertEquals(0, controller.countFavoritesForComponent(TEST_COMPONENT)) 635 assertEquals(emptyList<ControlInfo>(), controller.getFavoritesForComponent(TEST_COMPONENT)) 636 } 637 638 @Test 639 fun testReplaceFavoritesForStructure_differentComponentsAreFilteredOut() { 640 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 641 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2) 642 delayableExecutor.runAllReady() 643 644 assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT)) 645 assertEquals(listOf(TEST_CONTROL_INFO), 646 controller.getFavoritesForComponent(TEST_COMPONENT).flatMap { it.controls }) 647 } 648 649 @Test 650 fun testReplaceFavoritesForStructure_oldFavoritesRemoved() { 651 val controlInfo = ControlInfo("id", "title", "subtitle", 0) 652 assertNotEquals(TEST_CONTROL_INFO, controlInfo) 653 654 val newComponent = ComponentName("test.pkg", "test.class.3") 655 656 controller.replaceFavoritesForStructure( 657 StructureInfo( 658 newComponent, 659 "Home", 660 listOf(controlInfo) 661 )) 662 controller.replaceFavoritesForStructure( 663 StructureInfo( 664 newComponent, 665 "Home", 666 listOf(TEST_CONTROL_INFO) 667 )) 668 delayableExecutor.runAllReady() 669 670 assertEquals(1, controller.countFavoritesForComponent(newComponent)) 671 assertEquals(listOf(TEST_CONTROL_INFO), controller 672 .getFavoritesForComponent(newComponent).flatMap { it.controls }) 673 } 674 675 @Test 676 fun testReplaceFavoritesForStructure_favoritesInOrder() { 677 val controlInfo = ControlInfo("id", "title", "subtitle", 0) 678 679 val listOrder1 = listOf(TEST_CONTROL_INFO, controlInfo) 680 val structure1 = StructureInfo(TEST_COMPONENT, "Home", listOrder1) 681 controller.replaceFavoritesForStructure(structure1) 682 delayableExecutor.runAllReady() 683 684 assertEquals(2, controller.countFavoritesForComponent(TEST_COMPONENT)) 685 assertEquals(listOrder1, controller.getFavoritesForComponent(TEST_COMPONENT) 686 .flatMap { it.controls }) 687 688 val listOrder2 = listOf(controlInfo, TEST_CONTROL_INFO) 689 val structure2 = StructureInfo(TEST_COMPONENT, "Home", listOrder2) 690 691 controller.replaceFavoritesForStructure(structure2) 692 delayableExecutor.runAllReady() 693 694 assertEquals(2, controller.countFavoritesForComponent(TEST_COMPONENT)) 695 assertEquals(listOrder2, controller.getFavoritesForComponent(TEST_COMPONENT) 696 .flatMap { it.controls }) 697 } 698 699 @Test 700 fun testPackageRemoved_noFavorites_noRemovals() { 701 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 702 delayableExecutor.runAllReady() 703 704 val serviceInfo = mock(ServiceInfo::class.java) 705 `when`(serviceInfo.componentName).thenReturn(TEST_COMPONENT) 706 val info = ControlsServiceInfo(mContext, serviceInfo) 707 708 // Don't want to check what happens before this call 709 reset(persistenceWrapper) 710 listingCallbackCaptor.value.onServicesUpdated(listOf(info)) 711 delayableExecutor.runAllReady() 712 713 verify(bindingController, never()).onComponentRemoved(any()) 714 715 assertEquals(1, controller.getFavorites().size) 716 assertEquals(TEST_STRUCTURE_INFO, controller.getFavorites()[0]) 717 718 verify(persistenceWrapper, never()).storeFavorites(ArgumentMatchers.anyList()) 719 } 720 721 @Test 722 fun testPackageRemoved_hasFavorites() { 723 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 724 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2) 725 delayableExecutor.runAllReady() 726 727 val serviceInfo = mock(ServiceInfo::class.java) 728 `when`(serviceInfo.componentName).thenReturn(TEST_COMPONENT) 729 val info = ControlsServiceInfo(mContext, serviceInfo) 730 731 // Don't want to check what happens before this call 732 reset(persistenceWrapper) 733 734 listingCallbackCaptor.value.onServicesUpdated(listOf(info)) 735 delayableExecutor.runAllReady() 736 737 verify(bindingController).onComponentRemoved(TEST_COMPONENT_2) 738 739 assertEquals(1, controller.getFavorites().size) 740 assertEquals(TEST_STRUCTURE_INFO, controller.getFavorites()[0]) 741 742 verify(persistenceWrapper).storeFavorites(ArgumentMatchers.anyList()) 743 } 744 745 @Test 746 fun testExistingPackage_removedFromCache() { 747 `when`(auxiliaryPersistenceWrapper.favorites).thenReturn( 748 listOf(TEST_STRUCTURE_INFO, TEST_STRUCTURE_INFO_2)) 749 750 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 751 delayableExecutor.runAllReady() 752 753 val serviceInfo = mock(ServiceInfo::class.java) 754 `when`(serviceInfo.componentName).thenReturn(TEST_COMPONENT) 755 val info = ControlsServiceInfo(mContext, serviceInfo) 756 757 listingCallbackCaptor.value.onServicesUpdated(listOf(info)) 758 delayableExecutor.runAllReady() 759 760 verify(auxiliaryPersistenceWrapper).getCachedFavoritesAndRemoveFor(TEST_COMPONENT) 761 } 762 763 @Test 764 fun testAddedPackage_requestedFromCache() { 765 `when`(auxiliaryPersistenceWrapper.favorites).thenReturn( 766 listOf(TEST_STRUCTURE_INFO, TEST_STRUCTURE_INFO_2)) 767 768 val serviceInfo = mock(ServiceInfo::class.java) 769 `when`(serviceInfo.componentName).thenReturn(TEST_COMPONENT) 770 val info = ControlsServiceInfo(mContext, serviceInfo) 771 772 listingCallbackCaptor.value.onServicesUpdated(listOf(info)) 773 delayableExecutor.runAllReady() 774 775 verify(auxiliaryPersistenceWrapper).getCachedFavoritesAndRemoveFor(TEST_COMPONENT) 776 verify(auxiliaryPersistenceWrapper, never()) 777 .getCachedFavoritesAndRemoveFor(TEST_COMPONENT_2) 778 } 779 780 @Test 781 fun testSeedFavoritesForComponentsWithLimit() { 782 var responses = mutableListOf<SeedResponse>() 783 784 val controls1 = mutableListOf<Control>() 785 for (i in 1..10) { 786 controls1.add(statelessBuilderFromInfo(ControlInfo("id1:$i", TEST_CONTROL_TITLE, 787 TEST_CONTROL_SUBTITLE, TEST_DEVICE_TYPE), "testStructure").build()) 788 } 789 val controls2 = mutableListOf<Control>() 790 for (i in 1..3) { 791 controls2.add(statelessBuilderFromInfo(ControlInfo("id2:$i", TEST_CONTROL_TITLE, 792 TEST_CONTROL_SUBTITLE, TEST_DEVICE_TYPE), "testStructure2").build()) 793 } 794 controller.seedFavoritesForComponents(listOf(TEST_COMPONENT, TEST_COMPONENT_2), Consumer { 795 resp -> responses.add(resp) 796 }) 797 798 verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT), 799 capture(controlLoadCallbackCaptor)) 800 controlLoadCallbackCaptor.value.accept(controls1) 801 delayableExecutor.runAllReady() 802 803 verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT_2), 804 capture(controlLoadCallbackCaptor2)) 805 controlLoadCallbackCaptor2.value.accept(controls2) 806 delayableExecutor.runAllReady() 807 808 // COMPONENT 1 809 val structureInfo = controller.getFavoritesForComponent(TEST_COMPONENT)[0] 810 assertEquals(structureInfo.controls.size, 811 ControlsControllerImpl.SUGGESTED_CONTROLS_PER_STRUCTURE) 812 813 var i = 1 814 structureInfo.controls.forEach { 815 assertEquals(it.controlId, "id1:$i") 816 i++ 817 } 818 assertEquals(SeedResponse(TEST_COMPONENT.packageName, true), responses[0]) 819 820 // COMPONENT 2 821 val structureInfo2 = controller.getFavoritesForComponent(TEST_COMPONENT_2)[0] 822 assertEquals(structureInfo2.controls.size, 3) 823 824 i = 1 825 structureInfo2.controls.forEach { 826 assertEquals(it.controlId, "id2:$i") 827 i++ 828 } 829 assertEquals(SeedResponse(TEST_COMPONENT.packageName, true), responses[1]) 830 } 831 832 @Test 833 fun testSeedFavoritesForComponents_error() { 834 var response: SeedResponse? = null 835 836 controller.seedFavoritesForComponents(listOf(TEST_COMPONENT), Consumer { resp -> 837 response = resp 838 }) 839 840 verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT), 841 capture(controlLoadCallbackCaptor)) 842 843 controlLoadCallbackCaptor.value.error("Error loading") 844 845 delayableExecutor.runAllReady() 846 847 assertEquals(listOf<StructureInfo>(), controller.getFavoritesForComponent(TEST_COMPONENT)) 848 assertEquals(SeedResponse(TEST_COMPONENT.packageName, false), response) 849 } 850 851 @Test 852 fun testSeedFavoritesForComponents_inProgressCallback() { 853 var response: SeedResponse? = null 854 var seeded = false 855 val control = statelessBuilderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure) 856 .build() 857 858 controller.seedFavoritesForComponents(listOf(TEST_COMPONENT), Consumer { resp -> 859 response = resp 860 }) 861 862 verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT), 863 capture(controlLoadCallbackCaptor)) 864 865 controller.addSeedingFavoritesCallback(Consumer { accepted -> 866 seeded = accepted 867 }) 868 controlLoadCallbackCaptor.value.accept(listOf(control)) 869 870 delayableExecutor.runAllReady() 871 872 assertEquals(listOf(TEST_STRUCTURE_INFO), 873 controller.getFavoritesForComponent(TEST_COMPONENT)) 874 assertEquals(SeedResponse(TEST_COMPONENT.packageName, true), response) 875 assertTrue(seeded) 876 } 877 878 @Test 879 fun testRestoreReceiver_loadsAuxiliaryData() { 880 val receiver = controller.restoreFinishedReceiver 881 882 val structure1 = mock(StructureInfo::class.java) 883 val structure2 = mock(StructureInfo::class.java) 884 val listOfStructureInfo = listOf(structure1, structure2) 885 `when`(auxiliaryPersistenceWrapper.favorites).thenReturn(listOfStructureInfo) 886 887 val intent = Intent(BackupHelper.ACTION_RESTORE_FINISHED) 888 intent.putExtra(Intent.EXTRA_USER_ID, context.userId) 889 receiver.onReceive(context, intent) 890 delayableExecutor.runAllReady() 891 892 val inOrder = inOrder(auxiliaryPersistenceWrapper, persistenceWrapper) 893 inOrder.verify(auxiliaryPersistenceWrapper).initialize() 894 inOrder.verify(auxiliaryPersistenceWrapper).favorites 895 inOrder.verify(persistenceWrapper).storeFavorites(listOfStructureInfo) 896 inOrder.verify(persistenceWrapper).readFavorites() 897 } 898 899 @Test 900 fun testRestoreReceiver_noActionOnWrongUser() { 901 val receiver = controller.restoreFinishedReceiver 902 903 reset(persistenceWrapper) 904 reset(auxiliaryPersistenceWrapper) 905 val intent = Intent(BackupHelper.ACTION_RESTORE_FINISHED) 906 intent.putExtra(Intent.EXTRA_USER_ID, context.userId + 1) 907 receiver.onReceive(context, intent) 908 delayableExecutor.runAllReady() 909 910 verifyNoMoreInteractions(persistenceWrapper) 911 verifyNoMoreInteractions(auxiliaryPersistenceWrapper) 912 } 913 914 @Test 915 fun testGetFavoritesForStructure() { 916 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 917 controller.replaceFavoritesForStructure( 918 TEST_STRUCTURE_INFO_2.copy(componentName = TEST_COMPONENT)) 919 delayableExecutor.runAllReady() 920 921 assertEquals(TEST_STRUCTURE_INFO.controls, 922 controller.getFavoritesForStructure(TEST_COMPONENT, TEST_STRUCTURE)) 923 assertEquals(TEST_STRUCTURE_INFO_2.controls, 924 controller.getFavoritesForStructure(TEST_COMPONENT, TEST_STRUCTURE_2)) 925 } 926 927 @Test 928 fun testGetFavoritesForStructure_wrongStructure() { 929 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 930 delayableExecutor.runAllReady() 931 932 assertTrue(controller.getFavoritesForStructure(TEST_COMPONENT, TEST_STRUCTURE_2).isEmpty()) 933 } 934 935 @Test 936 fun testGetFavoritesForStructure_wrongComponent() { 937 controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) 938 delayableExecutor.runAllReady() 939 940 assertTrue(controller.getFavoritesForStructure(TEST_COMPONENT_2, TEST_STRUCTURE).isEmpty()) 941 } 942 943 @Test 944 fun testUserStructure() { 945 val userStructure = UserStructure(context, context.user, userFileManager) 946 verify(userFileManager, times(2)) 947 .getFile(ControlsFavoritePersistenceWrapper.FILE_NAME, context.user.identifier) 948 assertThat(userStructure.file).isNotNull() 949 } 950 951 @Test 952 fun testBindForPanel() { 953 controller.bindComponentForPanel(TEST_COMPONENT) 954 verify(bindingController).bindServiceForPanel(TEST_COMPONENT) 955 } 956 957 @Test 958 fun testRemoveFavoriteRemovesFavorite() { 959 val componentName = ComponentName(context, "test.Cls") 960 controller.addFavorite( 961 componentName, 962 "test structure", 963 ControlInfo( 964 controlId = "testId", 965 controlTitle = "Test Control", 966 controlSubtitle = "test control subtitle", 967 deviceType = DeviceTypes.TYPE_LIGHT, 968 ), 969 ) 970 971 controller.removeFavorites(componentName) 972 delayableExecutor.runAllReady() 973 974 verify(authorizedPanelsRepository) 975 .removeAuthorizedPanels(eq(setOf(componentName.packageName))) 976 assertThat(controller.getFavorites()).isEmpty() 977 } 978 } 979 980 private class DidRunRunnable() : Runnable { 981 var ran = false runnull982 override fun run() { 983 ran = true 984 } 985 } 986