• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.launcher3.model
18 
19 import android.util.Pair
20 import androidx.test.ext.junit.runners.AndroidJUnit4
21 import androidx.test.filters.SmallTest
22 import com.android.launcher3.model.data.ItemInfo
23 import com.android.launcher3.model.data.WorkspaceItemInfo
24 import com.android.launcher3.util.Executors
25 import com.android.launcher3.util.IntArray
26 import com.android.launcher3.util.TestUtil.runOnExecutorSync
27 import com.android.launcher3.util.any
28 import com.android.launcher3.util.eq
29 import com.android.launcher3.util.same
30 import com.google.common.truth.Truth.assertThat
31 import org.junit.After
32 import org.junit.Before
33 import org.junit.Test
34 import org.junit.runner.RunWith
35 import org.mockito.Mock
36 import org.mockito.Mockito.times
37 import org.mockito.Mockito.verify
38 import org.mockito.Mockito.verifyZeroInteractions
39 import org.mockito.Mockito.`when` as whenever
40 import org.mockito.MockitoAnnotations
41 
42 /** Tests for [AddWorkspaceItemsTask] */
43 @SmallTest
44 @RunWith(AndroidJUnit4::class)
45 class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
46 
47     private lateinit var mDataModelCallbacks: MyCallbacks
48 
49     @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
50 
51     @Before
setupnull52     override fun setup() {
53         super.setup()
54         MockitoAnnotations.initMocks(this)
55         mDataModelCallbacks = MyCallbacks()
56         Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) }
57             .get()
58     }
59 
60     @After
tearDownnull61     override fun tearDown() {
62         super.tearDown()
63     }
64 
65     @Test
givenNewItemAndNonEmptyPages_whenExecuteTask_thenAddNewItemnull66     fun givenNewItemAndNonEmptyPages_whenExecuteTask_thenAddNewItem() {
67         val itemToAdd = getNewItem()
68         val nonEmptyScreenIds = listOf(0, 1, 2)
69         givenNewItemSpaces(NewItemSpace(1, 2, 2))
70 
71         val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
72 
73         assertThat(addedItems.size).isEqualTo(1)
74         assertThat(addedItems.first().itemInfo.screenId).isEqualTo(1)
75         assertThat(addedItems.first().isAnimated).isTrue()
76         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 1)
77     }
78 
79     @Test
givenNewAndExistingItems_whenExecuteTask_thenOnlyAddNewItemnull80     fun givenNewAndExistingItems_whenExecuteTask_thenOnlyAddNewItem() {
81         val itemsToAdd = arrayOf(getNewItem(), getExistingItem())
82         givenNewItemSpaces(NewItemSpace(1, 0, 0))
83         val nonEmptyScreenIds = listOf(0)
84 
85         val addedItems = testAddItems(nonEmptyScreenIds, *itemsToAdd)
86 
87         assertThat(addedItems.size).isEqualTo(1)
88         assertThat(addedItems.first().itemInfo.screenId).isEqualTo(1)
89         assertThat(addedItems.first().isAnimated).isTrue()
90         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 1)
91     }
92 
93     @Test
givenOnlyExistingItem_whenExecuteTask_thenDoNotAddItemnull94     fun givenOnlyExistingItem_whenExecuteTask_thenDoNotAddItem() {
95         val itemToAdd = getExistingItem()
96         givenNewItemSpaces(NewItemSpace(1, 0, 0))
97         val nonEmptyScreenIds = listOf(0)
98 
99         val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
100 
101         assertThat(addedItems.size).isEqualTo(0)
102         verifyZeroInteractions(mWorkspaceItemSpaceFinder)
103     }
104 
105     @Test
givenNonSequentialScreenIds_whenExecuteTask_thenReturnNewScreenIdnull106     fun givenNonSequentialScreenIds_whenExecuteTask_thenReturnNewScreenId() {
107         val itemToAdd = getNewItem()
108         givenNewItemSpaces(NewItemSpace(2, 1, 3))
109         val nonEmptyScreenIds = listOf(0, 2, 3)
110 
111         val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
112 
113         assertThat(addedItems.size).isEqualTo(1)
114         assertThat(addedItems.first().itemInfo.screenId).isEqualTo(2)
115         assertThat(addedItems.first().isAnimated).isTrue()
116         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 1)
117     }
118 
119     @Test
givenMultipleItems_whenExecuteTask_thenAddThemnull120     fun givenMultipleItems_whenExecuteTask_thenAddThem() {
121         val itemsToAdd =
122             arrayOf(
123                 getNewItem(),
124                 getExistingItem(),
125                 getNewItem(),
126                 getNewItem(),
127                 getExistingItem(),
128             )
129         givenNewItemSpaces(
130             NewItemSpace(1, 3, 3),
131             NewItemSpace(2, 0, 0),
132             NewItemSpace(2, 0, 1),
133         )
134         val nonEmptyScreenIds = listOf(0, 1)
135 
136         val addedItems = testAddItems(nonEmptyScreenIds, *itemsToAdd)
137 
138         // Only the new items should be added
139         assertThat(addedItems.size).isEqualTo(3)
140 
141         // Items that are added to the first screen should not be animated
142         val itemsAddedToFirstScreen = addedItems.filter { it.itemInfo.screenId == 1 }
143         assertThat(itemsAddedToFirstScreen.size).isEqualTo(1)
144         assertThat(itemsAddedToFirstScreen.first().isAnimated).isFalse()
145 
146         // Items that are added to the second screen should be animated
147         val itemsAddedToSecondScreen = addedItems.filter { it.itemInfo.screenId == 2 }
148         assertThat(itemsAddedToSecondScreen.size).isEqualTo(2)
149         itemsAddedToSecondScreen.forEach { assertThat(it.isAnimated).isTrue() }
150         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 3)
151     }
152 
153     /** Sets up the item space data that will be returned from WorkspaceItemSpaceFinder. */
givenNewItemSpacesnull154     private fun givenNewItemSpaces(vararg newItemSpaces: NewItemSpace) {
155         val spaceStack = newItemSpaces.toMutableList()
156         whenever(
157                 mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any(), any(), any())
158             )
159             .then { spaceStack.removeFirst().toIntArray() }
160     }
161 
162     /**
163      * Verifies if WorkspaceItemSpaceFinder was called with proper arguments and how many times was
164      * it called.
165      */
verifyItemSpaceFinderCallnull166     private fun verifyItemSpaceFinderCall(nonEmptyScreenIds: List<Int>, numberOfExpectedCall: Int) {
167         verify(mWorkspaceItemSpaceFinder, times(numberOfExpectedCall))
168             .findSpaceForItem(
169                 same(mAppState),
170                 same(mModelHelper.bgDataModel),
171                 eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())),
172                 eq(IntArray()),
173                 eq(1),
174                 eq(1)
175             )
176     }
177 
178     /**
179      * Sets up the workspaces with items, executes the task, collects the added items from the model
180      * callback then returns it.
181      */
testAddItemsnull182     private fun testAddItems(
183         nonEmptyScreenIds: List<Int>,
184         vararg itemsToAdd: WorkspaceItemInfo
185     ): List<AddedItem> {
186         setupWorkspaces(nonEmptyScreenIds)
187         val task = newTask(*itemsToAdd)
188 
189         val addedItems = mutableListOf<AddedItem>()
190 
191         runOnExecutorSync(Executors.MODEL_EXECUTOR) {
192             mDataModelCallbacks.addedItems.clear()
193             mModelHelper.model.enqueueModelUpdateTask(task)
194             runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
195             addedItems.addAll(mDataModelCallbacks.addedItems)
196         }
197 
198         return addedItems
199     }
200 
201     /**
202      * Creates the task with the given items and replaces the WorkspaceItemSpaceFinder dependency
203      * with a mock.
204      */
newTasknull205     private fun newTask(vararg items: ItemInfo): AddWorkspaceItemsTask =
206         items
207             .map { Pair.create(it, Any()) }
208             .toMutableList()
<lambda>null209             .let { AddWorkspaceItemsTask(it, mWorkspaceItemSpaceFinder) }
210 }
211 
212 private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean)
213 
214 private class MyCallbacks : BgDataModel.Callbacks {
215 
216     val addedItems = mutableListOf<AddedItem>()
217 
bindAppsAddednull218     override fun bindAppsAdded(
219         newScreens: IntArray?,
220         addNotAnimated: ArrayList<ItemInfo>,
221         addAnimated: ArrayList<ItemInfo>
222     ) {
223         addedItems.addAll(addAnimated.map { AddedItem(it, true) })
224         addedItems.addAll(addNotAnimated.map { AddedItem(it, false) })
225     }
226 }
227