1 /* 2 * Copyright (C) 2023 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.qs.tiles.impl.custom.data.repository 18 19 import android.content.ComponentName 20 import android.graphics.drawable.Icon 21 import android.os.UserHandle 22 import android.service.quicksettings.Tile 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.filters.SmallTest 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.coroutines.collectValues 27 import com.android.systemui.kosmos.Kosmos 28 import com.android.systemui.kosmos.testScope 29 import com.android.systemui.qs.external.TileServiceKey 30 import com.android.systemui.qs.pipeline.shared.TileSpec 31 import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat 32 import com.android.systemui.qs.tiles.impl.custom.commons.copy 33 import com.android.systemui.qs.tiles.impl.custom.customTileSpec 34 import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister 35 import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults 36 import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade 37 import com.google.common.truth.Truth.assertThat 38 import kotlinx.coroutines.ExperimentalCoroutinesApi 39 import kotlinx.coroutines.flow.first 40 import kotlinx.coroutines.test.runCurrent 41 import kotlinx.coroutines.test.runTest 42 import org.junit.Test 43 import org.junit.runner.RunWith 44 45 @SmallTest 46 @RunWith(AndroidJUnit4::class) 47 @OptIn(ExperimentalCoroutinesApi::class) 48 class CustomTileRepositoryTest : SysuiTestCase() { 49 <lambda>null50 private val kosmos = Kosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) } 51 private val underTest: CustomTileRepository = <lambda>null52 with(kosmos) { 53 CustomTileRepositoryImpl( 54 customTileSpec, 55 customTileStatePersister, 56 packageManagerAdapterFacade.packageManagerAdapter, 57 testScope.testScheduler, 58 ) 59 } 60 61 @Test persistableTileIsRestoredForUsernull62 fun persistableTileIsRestoredForUser() = 63 with(kosmos) { 64 testScope.runTest { 65 customTileStatePersister.persistState(TEST_TILE_KEY_1, TEST_TILE_1) 66 customTileStatePersister.persistState(TEST_TILE_KEY_2, TEST_TILE_2) 67 68 underTest.restoreForTheUserIfNeeded(TEST_USER_1, true) 69 runCurrent() 70 71 assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1) 72 assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1) 73 } 74 } 75 76 @Test notPersistableTileIsNotRestorednull77 fun notPersistableTileIsNotRestored() = 78 with(kosmos) { 79 testScope.runTest { 80 customTileStatePersister.persistState(TEST_TILE_KEY_1, TEST_TILE_1) 81 val tiles = collectValues(underTest.getTiles(TEST_USER_1)) 82 83 underTest.restoreForTheUserIfNeeded(TEST_USER_1, false) 84 runCurrent() 85 86 assertThat(tiles()).isEmpty() 87 } 88 } 89 90 @Test emptyPersistedStateIsHandlednull91 fun emptyPersistedStateIsHandled() = 92 with(kosmos) { 93 testScope.runTest { 94 val tiles = collectValues(underTest.getTiles(TEST_USER_1)) 95 96 underTest.restoreForTheUserIfNeeded(TEST_USER_1, true) 97 runCurrent() 98 99 assertThat(tiles()).isEmpty() 100 } 101 } 102 103 @Test updatingWithPersistableTilePersistsnull104 fun updatingWithPersistableTilePersists() = 105 with(kosmos) { 106 testScope.runTest { 107 underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true) 108 runCurrent() 109 110 assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1)) 111 .isEqualTo(TEST_TILE_1) 112 } 113 } 114 115 @Test updatingWithNotPersistableTileDoesntPersistnull116 fun updatingWithNotPersistableTileDoesntPersist() = 117 with(kosmos) { 118 testScope.runTest { 119 underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, false) 120 runCurrent() 121 122 assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1)).isNull() 123 } 124 } 125 126 @Test updateWithTileEmitsnull127 fun updateWithTileEmits() = 128 with(kosmos) { 129 testScope.runTest { 130 underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true) 131 runCurrent() 132 133 assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1) 134 assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1) 135 } 136 } 137 138 @Test updatingPeristableWithDefaultsPersistsnull139 fun updatingPeristableWithDefaultsPersists() = 140 with(kosmos) { 141 testScope.runTest { 142 underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true) 143 runCurrent() 144 145 assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1)) 146 .isEqualTo(TEST_TILE_1) 147 } 148 } 149 150 @Test updatingNotPersistableWithDefaultsDoesntPersistnull151 fun updatingNotPersistableWithDefaultsDoesntPersist() = 152 with(kosmos) { 153 testScope.runTest { 154 underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, false) 155 runCurrent() 156 157 assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1)).isNull() 158 } 159 } 160 161 @Test updatingPeristableWithErrorDefaultsDoesntPersistnull162 fun updatingPeristableWithErrorDefaultsDoesntPersist() = 163 with(kosmos) { 164 testScope.runTest { 165 underTest.updateWithDefaults(TEST_USER_1, CustomTileDefaults.Error, true) 166 runCurrent() 167 168 assertThat(customTileStatePersister.readState(TEST_TILE_KEY_1)).isNull() 169 } 170 } 171 172 @Test updateWithDefaultsEmitsnull173 fun updateWithDefaultsEmits() = 174 with(kosmos) { 175 testScope.runTest { 176 underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true) 177 runCurrent() 178 179 assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1) 180 assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1) 181 } 182 } 183 184 @Test getTileForAnotherUserReturnsNullnull185 fun getTileForAnotherUserReturnsNull() = 186 with(kosmos) { 187 testScope.runTest { 188 underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true) 189 runCurrent() 190 191 assertThat(underTest.getTile(TEST_USER_2)).isNull() 192 } 193 } 194 195 @Test getTilesForAnotherUserEmptynull196 fun getTilesForAnotherUserEmpty() = 197 with(kosmos) { 198 testScope.runTest { 199 val tiles = collectValues(underTest.getTiles(TEST_USER_2)) 200 201 underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true) 202 runCurrent() 203 204 assertThat(tiles()).isEmpty() 205 } 206 } 207 208 @Test updatingWithTileForTheSameUserAddsDatanull209 fun updatingWithTileForTheSameUserAddsData() = 210 with(kosmos) { 211 testScope.runTest { 212 underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true) 213 runCurrent() 214 215 underTest.updateWithTile( 216 TEST_USER_1, 217 Tile().apply { subtitle = "test_subtitle" }, 218 true 219 ) 220 runCurrent() 221 222 val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" } 223 assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile) 224 assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile) 225 } 226 } 227 228 @Test updatingWithTileForAnotherUserOverridesTilenull229 fun updatingWithTileForAnotherUserOverridesTile() = 230 with(kosmos) { 231 testScope.runTest { 232 underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true) 233 runCurrent() 234 235 val tiles = collectValues(underTest.getTiles(TEST_USER_2)) 236 underTest.updateWithTile(TEST_USER_2, TEST_TILE_2, true) 237 runCurrent() 238 239 assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2) 240 assertThat(tiles()).hasSize(1) 241 assertThat(tiles().last()).isEqualTo(TEST_TILE_2) 242 } 243 } 244 245 @Test updatingWithDefaultsForTheSameUserAddsDatanull246 fun updatingWithDefaultsForTheSameUserAddsData() = 247 with(kosmos) { 248 testScope.runTest { 249 underTest.updateWithTile( 250 TEST_USER_1, 251 Tile().apply { subtitle = "test_subtitle" }, 252 true 253 ) 254 runCurrent() 255 256 underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true) 257 runCurrent() 258 259 val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" } 260 assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile) 261 assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile) 262 } 263 } 264 265 @Test updatingWithDefaultsForAnotherUserOverridesTilenull266 fun updatingWithDefaultsForAnotherUserOverridesTile() = 267 with(kosmos) { 268 testScope.runTest { 269 underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true) 270 runCurrent() 271 272 val tiles = collectValues(underTest.getTiles(TEST_USER_2)) 273 underTest.updateWithDefaults(TEST_USER_2, TEST_DEFAULTS_2, true) 274 runCurrent() 275 276 assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2) 277 assertThat(tiles()).hasSize(1) 278 assertThat(tiles().last()).isEqualTo(TEST_TILE_2) 279 } 280 } 281 282 @Test isActiveFollowsPackageManagerAdapter_user0null283 fun isActiveFollowsPackageManagerAdapter_user0() = 284 with(kosmos) { 285 testScope.runTest { 286 packageManagerAdapterFacade.setExclusiveForUser(0) 287 288 underTest.updateWithDefaults(UserHandle.of(0), TEST_DEFAULTS_1, true) 289 packageManagerAdapterFacade.setIsActive(false) 290 assertThat(underTest.isTileActive()).isFalse() 291 292 packageManagerAdapterFacade.setIsActive(true) 293 assertThat(underTest.isTileActive()).isTrue() 294 } 295 } 296 297 @Test isToggleableFollowsPackageManagerAdapternull298 fun isToggleableFollowsPackageManagerAdapter() = 299 with(kosmos) { 300 testScope.runTest { 301 underTest.updateWithDefaults(UserHandle.of(0), TEST_DEFAULTS_1, true) 302 packageManagerAdapterFacade.setIsToggleable(false) 303 assertThat(underTest.isTileToggleable()).isFalse() 304 305 packageManagerAdapterFacade.setIsToggleable(true) 306 assertThat(underTest.isTileToggleable()).isTrue() 307 } 308 } 309 310 @Test isActiveFollowsPackageManagerAdapter_user10_withAdapterForUser10null311 fun isActiveFollowsPackageManagerAdapter_user10_withAdapterForUser10() = 312 with(kosmos) { 313 testScope.runTest { 314 packageManagerAdapterFacade.setExclusiveForUser(10) 315 316 underTest.updateWithDefaults(UserHandle.of(10), TEST_DEFAULTS_1, true) 317 packageManagerAdapterFacade.setIsActive(false) 318 assertThat(underTest.isTileActive()).isFalse() 319 320 packageManagerAdapterFacade.setIsActive(true) 321 assertThat(underTest.isTileActive()).isTrue() 322 } 323 } 324 325 @Test isToggleableFollowsPackageManagerAdapter_user10_withAdapterForUser10null326 fun isToggleableFollowsPackageManagerAdapter_user10_withAdapterForUser10() = 327 with(kosmos) { 328 testScope.runTest { 329 packageManagerAdapterFacade.setExclusiveForUser(10) 330 331 underTest.updateWithDefaults(UserHandle.of(10), TEST_DEFAULTS_1, true) 332 packageManagerAdapterFacade.setIsToggleable(false) 333 assertThat(underTest.isTileToggleable()).isFalse() 334 335 packageManagerAdapterFacade.setIsToggleable(true) 336 assertThat(underTest.isTileToggleable()).isTrue() 337 } 338 } 339 340 @Test isActiveDoesntFollowPackageManagerAdapter_user10null341 fun isActiveDoesntFollowPackageManagerAdapter_user10() = 342 with(kosmos) { 343 testScope.runTest { 344 packageManagerAdapterFacade.setExclusiveForUser(0) 345 346 underTest.updateWithDefaults(UserHandle.of(10), TEST_DEFAULTS_1, true) 347 packageManagerAdapterFacade.setIsActive(false) 348 assertThat(underTest.isTileActive()).isFalse() 349 350 packageManagerAdapterFacade.setIsActive(true) 351 assertThat(underTest.isTileActive()).isFalse() 352 } 353 } 354 355 @Test isToggleableDoesntFollowPackageManagerAdapter_user10null356 fun isToggleableDoesntFollowPackageManagerAdapter_user10() = 357 with(kosmos) { 358 testScope.runTest { 359 packageManagerAdapterFacade.setExclusiveForUser(0) 360 361 underTest.updateWithDefaults(UserHandle.of(10), TEST_DEFAULTS_1, true) 362 packageManagerAdapterFacade.setIsToggleable(false) 363 assertThat(underTest.isTileToggleable()).isFalse() 364 365 packageManagerAdapterFacade.setIsToggleable(true) 366 assertThat(underTest.isTileToggleable()).isFalse() 367 } 368 } 369 370 private companion object { 371 372 val TEST_COMPONENT = ComponentName("test.pkg", "test.cls") 373 374 val TEST_USER_1 = UserHandle.of(1)!! <lambda>null375 val TEST_TILE_1 by lazy { 376 Tile().apply { 377 label = "test_tile_1" 378 icon = Icon.createWithContentUri("file://test_1") 379 } 380 } 381 val TEST_TILE_KEY_1 = TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier) <lambda>null382 val TEST_DEFAULTS_1 by lazy { 383 CustomTileDefaults.Result(TEST_TILE_1.icon, TEST_TILE_1.label) 384 } 385 386 val TEST_USER_2 = UserHandle.of(2)!! <lambda>null387 val TEST_TILE_2 by lazy { 388 Tile().apply { 389 label = "test_tile_2" 390 icon = Icon.createWithContentUri("file://test_2") 391 } 392 } 393 val TEST_TILE_KEY_2 = TileServiceKey(TEST_COMPONENT, TEST_USER_2.identifier) <lambda>null394 val TEST_DEFAULTS_2 by lazy { 395 CustomTileDefaults.Result(TEST_TILE_2.icon, TEST_TILE_2.label) 396 } 397 } 398 } 399