1 package com.android.launcher3.model
2
3 import android.appwidget.AppWidgetManager
4 import android.content.Intent
5 import android.os.UserHandle
6 import android.platform.test.flag.junit.SetFlagsRule
7 import android.provider.Settings
8 import androidx.test.ext.junit.runners.AndroidJUnit4
9 import androidx.test.filters.SmallTest
10 import com.android.dx.mockito.inline.extended.ExtendedMockito
11 import com.android.launcher3.Flags
12 import com.android.launcher3.InvariantDeviceProfile
13 import com.android.launcher3.LauncherAppState
14 import com.android.launcher3.LauncherModel
15 import com.android.launcher3.LauncherModel.LoaderTransaction
16 import com.android.launcher3.icons.IconCache
17 import com.android.launcher3.icons.cache.CachingLogic
18 import com.android.launcher3.icons.cache.IconCacheUpdateHandler
19 import com.android.launcher3.pm.UserCache
20 import com.android.launcher3.provider.RestoreDbTask
21 import com.android.launcher3.ui.TestViewHelpers
22 import com.android.launcher3.util.Executors.MODEL_EXECUTOR
23 import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
24 import com.android.launcher3.util.LooperIdleLock
25 import com.android.launcher3.util.UserIconInfo
26 import com.google.common.truth.Truth
27 import java.util.concurrent.CountDownLatch
28 import junit.framework.Assert.assertEquals
29 import org.junit.After
30 import org.junit.Before
31 import org.junit.Rule
32 import org.junit.Test
33 import org.junit.runner.RunWith
34 import org.mockito.ArgumentCaptor
35 import org.mockito.ArgumentMatchers.any
36 import org.mockito.ArgumentMatchers.anyInt
37 import org.mockito.ArgumentMatchers.anyList
38 import org.mockito.ArgumentMatchers.anyMap
39 import org.mockito.Mock
40 import org.mockito.Mockito
41 import org.mockito.Mockito.times
42 import org.mockito.Mockito.`when`
43 import org.mockito.MockitoAnnotations
44 import org.mockito.MockitoSession
45 import org.mockito.Spy
46 import org.mockito.kotlin.anyOrNull
47 import org.mockito.kotlin.doReturn
48 import org.mockito.kotlin.spy
49 import org.mockito.kotlin.verify
50 import org.mockito.kotlin.whenever
51 import org.mockito.quality.Strictness
52
53 private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql"
54
55 @SmallTest
56 @RunWith(AndroidJUnit4::class)
57 class LoaderTaskTest {
58 private var context = SandboxModelContext()
59 private val expectedBroadcastModel =
60 FirstScreenBroadcastModel(
61 installerPackage = "installerPackage",
62 pendingCollectionItems = mutableSetOf("pendingCollectionItem"),
63 pendingWidgetItems = mutableSetOf("pendingWidgetItem"),
64 pendingHotseatItems = mutableSetOf("pendingHotseatItem"),
65 pendingWorkspaceItems = mutableSetOf("pendingWorkspaceItem"),
66 installedHotseatItems = mutableSetOf("installedHotseatItem"),
67 installedWorkspaceItems = mutableSetOf("installedWorkspaceItem"),
68 firstScreenInstalledWidgets = mutableSetOf("installedFirstScreenWidget"),
69 secondaryScreenInstalledWidgets = mutableSetOf("installedSecondaryScreenWidget")
70 )
71 private lateinit var mockitoSession: MockitoSession
72
73 @Mock private lateinit var app: LauncherAppState
74 @Mock private lateinit var bgAllAppsList: AllAppsList
75 @Mock private lateinit var modelDelegate: ModelDelegate
76 @Mock private lateinit var launcherBinder: BaseLauncherBinder
77 @Mock private lateinit var launcherModel: LauncherModel
78 @Mock private lateinit var transaction: LoaderTransaction
79 @Mock private lateinit var iconCache: IconCache
80 @Mock private lateinit var idleLock: LooperIdleLock
81 @Mock private lateinit var iconCacheUpdateHandler: IconCacheUpdateHandler
82 @Mock private lateinit var userCache: UserCache
83
84 @Spy private var userManagerState: UserManagerState? = UserManagerState()
85
86 @get:Rule val setFlagsRule = SetFlagsRule()
87
88 @Before
setupnull89 fun setup() {
90 MockitoAnnotations.initMocks(this)
91 mockitoSession =
92 ExtendedMockito.mockitoSession()
93 .strictness(Strictness.LENIENT)
94 .mockStatic(FirstScreenBroadcastHelper::class.java)
95 .startMocking()
96 val idp =
97 InvariantDeviceProfile().apply {
98 numRows = 5
99 numColumns = 6
100 numDatabaseHotseatIcons = 5
101 }
102 context.putObject(InvariantDeviceProfile.INSTANCE, idp)
103 context.putObject(LauncherAppState.INSTANCE, app)
104
105 doReturn(TestViewHelpers.findWidgetProvider(false))
106 .`when`(context.spyService(AppWidgetManager::class.java))
107 .getAppWidgetInfo(anyInt())
108 `when`(app.context).thenReturn(context)
109 `when`(app.model).thenReturn(launcherModel)
110 `when`(launcherModel.beginLoader(any(LoaderTask::class.java))).thenReturn(transaction)
111 `when`(app.iconCache).thenReturn(iconCache)
112 `when`(launcherModel.modelDbController)
113 .thenReturn(FactitiousDbController(context, INSERTION_STATEMENT_FILE))
114 `when`(app.invariantDeviceProfile).thenReturn(idp)
115 `when`(launcherBinder.newIdleLock(any(LoaderTask::class.java))).thenReturn(idleLock)
116 `when`(idleLock.awaitLocked(1000)).thenReturn(false)
117 `when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler)
118 context.putObject(UserCache.INSTANCE, userCache)
119 }
120
121 @After
tearDownnull122 fun tearDown() {
123 context.onDestroy()
124 mockitoSession.finishMocking()
125 }
126
127 @Test
loadsDataProperlynull128 fun loadsDataProperly() =
129 with(BgDataModel()) {
130 val MAIN_HANDLE = UserHandle.of(0)
131 val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
132 `when`(userCache.userProfiles).thenReturn(mockUserHandles)
133 `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
134 LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder)
135 .runSyncOnBackgroundThread()
136 Truth.assertThat(workspaceItems.size).isAtLeast(25)
137 Truth.assertThat(appWidgets.size).isAtLeast(7)
138 Truth.assertThat(collections.size()).isAtLeast(8)
139 Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
140 }
141
142 @Test
bindsLoadedDataCorrectlynull143 fun bindsLoadedDataCorrectly() {
144 LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
145 .runSyncOnBackgroundThread()
146
147 verify(launcherBinder).bindWorkspace(true, false)
148 verify(modelDelegate).workspaceLoadComplete()
149 verify(modelDelegate).loadAndBindAllAppsItems(any(), any(), any())
150 verify(launcherBinder).bindAllApps()
151 verify(iconCacheUpdateHandler, times(4)).updateIcons(any(), any<CachingLogic<Any>>(), any())
152 verify(launcherBinder).bindDeepShortcuts()
153 verify(launcherBinder).bindWidgets()
154 verify(modelDelegate).loadAndBindOtherItems(any())
155 verify(iconCacheUpdateHandler).finish()
156 verify(modelDelegate).modelLoadComplete()
157 verify(transaction).commit()
158 }
159
160 @Test
setsQuietModeFlagCorrectlyForWorkProfilenull161 fun setsQuietModeFlagCorrectlyForWorkProfile() =
162 with(BgDataModel()) {
163 setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE)
164 val MAIN_HANDLE = UserHandle.of(0)
165 val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
166 `when`(userCache.userProfiles).thenReturn(mockUserHandles)
167 `when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
168 `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
169
170 LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
171 .runSyncOnBackgroundThread()
172
173 verify(bgAllAppsList)
174 .setFlags(BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, true)
175 verify(bgAllAppsList)
176 .setFlags(BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, false)
177 verify(bgAllAppsList, Mockito.never())
178 .setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true)
179 }
180
181 @Test
setsQuietModeFlagCorrectlyForPrivateProfilenull182 fun setsQuietModeFlagCorrectlyForPrivateProfile() =
183 with(BgDataModel()) {
184 setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE)
185 val MAIN_HANDLE = UserHandle.of(0)
186 val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
187 `when`(userCache.userProfiles).thenReturn(mockUserHandles)
188 `when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
189 `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 3))
190
191 LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
192 .runSyncOnBackgroundThread()
193
194 verify(bgAllAppsList)
195 .setFlags(BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, false)
196 verify(bgAllAppsList)
197 .setFlags(BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, true)
198 verify(bgAllAppsList, Mockito.never())
199 .setFlags(BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED, true)
200 }
201
202 @Test
When launcher_broadcast_installed_apps and is restore then send installed item broadcastnull203 fun `When launcher_broadcast_installed_apps and is restore then send installed item broadcast`() {
204 // Given
205 val spyContext = spy(context)
206 `when`(app.context).thenReturn(spyContext)
207 whenever(
208 FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
209 anyOrNull(),
210 anyList(),
211 anyMap(),
212 anyList()
213 )
214 )
215 .thenReturn(listOf(expectedBroadcastModel))
216
217 whenever(
218 FirstScreenBroadcastHelper.sendBroadcastsForModels(
219 spyContext,
220 listOf(expectedBroadcastModel)
221 )
222 )
223 .thenCallRealMethod()
224
225 Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 1)
226 RestoreDbTask.setPending(spyContext)
227
228 // When
229 LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
230 .runSyncOnBackgroundThread()
231
232 // Then
233 val argumentCaptor = ArgumentCaptor.forClass(Intent::class.java)
234 verify(spyContext).sendBroadcast(argumentCaptor.capture())
235 val actualBroadcastIntent = argumentCaptor.value
236 assertEquals(expectedBroadcastModel.installerPackage, actualBroadcastIntent.`package`)
237 assertEquals(
238 ArrayList(expectedBroadcastModel.installedWorkspaceItems),
239 actualBroadcastIntent.getStringArrayListExtra("workspaceInstalledItems")
240 )
241 assertEquals(
242 ArrayList(expectedBroadcastModel.installedHotseatItems),
243 actualBroadcastIntent.getStringArrayListExtra("hotseatInstalledItems")
244 )
245 assertEquals(
246 ArrayList(
247 expectedBroadcastModel.firstScreenInstalledWidgets +
248 expectedBroadcastModel.secondaryScreenInstalledWidgets
249 ),
250 actualBroadcastIntent.getStringArrayListExtra("widgetInstalledItems")
251 )
252 assertEquals(
253 ArrayList(expectedBroadcastModel.pendingCollectionItems),
254 actualBroadcastIntent.getStringArrayListExtra("folderItem")
255 )
256 assertEquals(
257 ArrayList(expectedBroadcastModel.pendingWorkspaceItems),
258 actualBroadcastIntent.getStringArrayListExtra("workspaceItem")
259 )
260 assertEquals(
261 ArrayList(expectedBroadcastModel.pendingHotseatItems),
262 actualBroadcastIntent.getStringArrayListExtra("hotseatItem")
263 )
264 assertEquals(
265 ArrayList(expectedBroadcastModel.pendingWidgetItems),
266 actualBroadcastIntent.getStringArrayListExtra("widgetItem")
267 )
268 }
269
270 @Test
When not a restore then installed item broadcast not sentnull271 fun `When not a restore then installed item broadcast not sent`() {
272 // Given
273 val spyContext = spy(context)
274 `when`(app.context).thenReturn(spyContext)
275 whenever(
276 FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
277 anyOrNull(),
278 anyList(),
279 anyMap(),
280 anyList()
281 )
282 )
283 .thenReturn(listOf(expectedBroadcastModel))
284
285 whenever(
286 FirstScreenBroadcastHelper.sendBroadcastsForModels(
287 spyContext,
288 listOf(expectedBroadcastModel)
289 )
290 )
291 .thenCallRealMethod()
292
293 Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 1)
294
295 // When
296 LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
297 .runSyncOnBackgroundThread()
298
299 // Then
300 verify(spyContext, times(0)).sendBroadcast(any(Intent::class.java))
301 }
302
303 @Test
When launcher_broadcast_installed_apps false then installed item broadcast not sentnull304 fun `When launcher_broadcast_installed_apps false then installed item broadcast not sent`() {
305 // Given
306 val spyContext = spy(context)
307 `when`(app.context).thenReturn(spyContext)
308 whenever(
309 FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
310 anyOrNull(),
311 anyList(),
312 anyMap(),
313 anyList()
314 )
315 )
316 .thenReturn(listOf(expectedBroadcastModel))
317
318 whenever(
319 FirstScreenBroadcastHelper.sendBroadcastsForModels(
320 spyContext,
321 listOf(expectedBroadcastModel)
322 )
323 )
324 .thenCallRealMethod()
325
326 Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 0)
327 RestoreDbTask.setPending(spyContext)
328
329 // When
330 LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
331 .runSyncOnBackgroundThread()
332
333 // Then
334 verify(spyContext, times(0)).sendBroadcast(any(Intent::class.java))
335 }
336 }
337
runSyncOnBackgroundThreadnull338 private fun LoaderTask.runSyncOnBackgroundThread() {
339 val latch = CountDownLatch(1)
340 MODEL_EXECUTOR.execute {
341 run()
342 latch.countDown()
343 }
344 latch.await()
345 }
346