• 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.any
27 import com.android.launcher3.util.eq
28 import com.android.launcher3.util.same
29 import com.google.common.truth.Truth.assertThat
30 import org.junit.After
31 import org.junit.Before
32 import org.junit.Test
33 import org.junit.runner.RunWith
34 import org.mockito.ArgumentCaptor
35 import org.mockito.Captor
36 import org.mockito.Mock
37 import org.mockito.Mockito.times
38 import org.mockito.Mockito.verify
39 import org.mockito.Mockito.verifyZeroInteractions
40 import org.mockito.Mockito.`when` as whenever
41 import org.mockito.MockitoAnnotations
42 
43 /** Tests for [AddWorkspaceItemsTask] */
44 @SmallTest
45 @RunWith(AndroidJUnit4::class)
46 class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
47 
48     @Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
49 
50     @Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
51 
52     @Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks
53 
54     @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
55 
56     @Before
setupnull57     override fun setup() {
58         super.setup()
59         MockitoAnnotations.initMocks(this)
60         Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) }
61             .get()
62     }
63 
64     @After
tearDownnull65     override fun tearDown() {
66         super.tearDown()
67     }
68 
69     @Test
givenNewItemAndNonEmptyPages_whenExecuteTask_thenAddNewItemnull70     fun givenNewItemAndNonEmptyPages_whenExecuteTask_thenAddNewItem() {
71         val itemToAdd = getNewItem()
72         val nonEmptyScreenIds = listOf(0, 1, 2)
73         givenNewItemSpaces(NewItemSpace(1, 2, 2))
74 
75         val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
76 
77         assertThat(addedItems.size).isEqualTo(1)
78         assertThat(addedItems.first().itemInfo.screenId).isEqualTo(1)
79         assertThat(addedItems.first().isAnimated).isTrue()
80         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 1)
81     }
82 
83     @Test
givenNewAndExistingItems_whenExecuteTask_thenOnlyAddNewItemnull84     fun givenNewAndExistingItems_whenExecuteTask_thenOnlyAddNewItem() {
85         val itemsToAdd = arrayOf(getNewItem(), getExistingItem())
86         givenNewItemSpaces(NewItemSpace(1, 0, 0))
87         val nonEmptyScreenIds = listOf(0)
88 
89         val addedItems = testAddItems(nonEmptyScreenIds, *itemsToAdd)
90 
91         assertThat(addedItems.size).isEqualTo(1)
92         assertThat(addedItems.first().itemInfo.screenId).isEqualTo(1)
93         assertThat(addedItems.first().isAnimated).isTrue()
94         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 1)
95     }
96 
97     @Test
givenOnlyExistingItem_whenExecuteTask_thenDoNotAddItemnull98     fun givenOnlyExistingItem_whenExecuteTask_thenDoNotAddItem() {
99         val itemToAdd = getExistingItem()
100         givenNewItemSpaces(NewItemSpace(1, 0, 0))
101         val nonEmptyScreenIds = listOf(0)
102 
103         val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
104 
105         assertThat(addedItems.size).isEqualTo(0)
106         verifyZeroInteractions(mWorkspaceItemSpaceFinder, mDataModelCallbacks)
107     }
108 
109     @Test
givenNonSequentialScreenIds_whenExecuteTask_thenReturnNewScreenIdnull110     fun givenNonSequentialScreenIds_whenExecuteTask_thenReturnNewScreenId() {
111         val itemToAdd = getNewItem()
112         givenNewItemSpaces(NewItemSpace(2, 1, 3))
113         val nonEmptyScreenIds = listOf(0, 2, 3)
114 
115         val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
116 
117         assertThat(addedItems.size).isEqualTo(1)
118         assertThat(addedItems.first().itemInfo.screenId).isEqualTo(2)
119         assertThat(addedItems.first().isAnimated).isTrue()
120         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 1)
121     }
122 
123     @Test
givenMultipleItems_whenExecuteTask_thenAddThemnull124     fun givenMultipleItems_whenExecuteTask_thenAddThem() {
125         val itemsToAdd =
126             arrayOf(
127                 getNewItem(),
128                 getExistingItem(),
129                 getNewItem(),
130                 getNewItem(),
131                 getExistingItem(),
132             )
133         givenNewItemSpaces(
134             NewItemSpace(1, 3, 3),
135             NewItemSpace(2, 0, 0),
136             NewItemSpace(2, 0, 1),
137         )
138         val nonEmptyScreenIds = listOf(0, 1)
139 
140         val addedItems = testAddItems(nonEmptyScreenIds, *itemsToAdd)
141 
142         // Only the new items should be added
143         assertThat(addedItems.size).isEqualTo(3)
144 
145         // Items that are added to the first screen should not be animated
146         val itemsAddedToFirstScreen = addedItems.filter { it.itemInfo.screenId == 1 }
147         assertThat(itemsAddedToFirstScreen.size).isEqualTo(1)
148         assertThat(itemsAddedToFirstScreen.first().isAnimated).isFalse()
149 
150         // Items that are added to the second screen should be animated
151         val itemsAddedToSecondScreen = addedItems.filter { it.itemInfo.screenId == 2 }
152         assertThat(itemsAddedToSecondScreen.size).isEqualTo(2)
153         itemsAddedToSecondScreen.forEach { assertThat(it.isAnimated).isTrue() }
154         verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 3)
155     }
156 
157     /** Sets up the item space data that will be returned from WorkspaceItemSpaceFinder. */
givenNewItemSpacesnull158     private fun givenNewItemSpaces(vararg newItemSpaces: NewItemSpace) {
159         val spaceStack = newItemSpaces.toMutableList()
160         whenever(
161                 mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any(), any(), any())
162             )
163             .then { spaceStack.removeFirst().toIntArray() }
164     }
165 
166     /**
167      * Verifies if WorkspaceItemSpaceFinder was called with proper arguments and how many times was
168      * it called.
169      */
verifyItemSpaceFinderCallnull170     private fun verifyItemSpaceFinderCall(nonEmptyScreenIds: List<Int>, numberOfExpectedCall: Int) {
171         verify(mWorkspaceItemSpaceFinder, times(numberOfExpectedCall))
172             .findSpaceForItem(
173                 same(mAppState),
174                 same(mModelHelper.bgDataModel),
175                 eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())),
176                 eq(IntArray()),
177                 eq(1),
178                 eq(1)
179             )
180     }
181 
182     /**
183      * Sets up the workspaces with items, executes the task, collects the added items from the model
184      * callback then returns it.
185      */
testAddItemsnull186     private fun testAddItems(
187         nonEmptyScreenIds: List<Int>,
188         vararg itemsToAdd: WorkspaceItemInfo
189     ): List<AddedItem> {
190         setupWorkspaces(nonEmptyScreenIds)
191         val task = newTask(*itemsToAdd)
192         var updateCount = 0
193         mModelHelper.executeTaskForTest(task).forEach {
194             updateCount++
195             it.run()
196         }
197 
198         val addedItems = mutableListOf<AddedItem>()
199         if (updateCount > 0) {
200             verify(mDataModelCallbacks)
201                 .bindAppsAdded(
202                     any(),
203                     mNotAnimatedItemArgumentCaptor.capture(),
204                     mAnimatedItemArgumentCaptor.capture()
205                 )
206             addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) })
207             addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) })
208         }
209 
210         return addedItems
211     }
212 
213     /**
214      * Creates the task with the given items and replaces the WorkspaceItemSpaceFinder dependency
215      * with a mock.
216      */
newTasknull217     private fun newTask(vararg items: ItemInfo): AddWorkspaceItemsTask =
218         items
219             .map { Pair.create(it, Any()) }
220             .toMutableList()
<lambda>null221             .let { AddWorkspaceItemsTask(it, mWorkspaceItemSpaceFinder) }
222 }
223 
224 private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean)
225