Merge "Revert "Migrate workspace item adding tests to kotlin"" into sc-v2-dev

This commit is contained in:
TreeHugger Robot 2021-11-25 15:40:06 +00:00 committed by Android (Google) Code Review
commit da014976cd
3 changed files with 202 additions and 326 deletions

View File

@ -23,7 +23,7 @@ package {
// Source code used for test
filegroup {
name: "launcher-tests-src",
srcs: ["src/**/*.java", "src/**/*.kt"],
srcs: ["src/**/*.java"],
}
// Source code used for oop test helpers

View File

@ -0,0 +1,201 @@
package com.android.launcher3.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.util.Pair;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.LauncherModelHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.List;
/**
* Tests for {@link AddWorkspaceItemsTask}
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AddWorkspaceItemsTaskTest {
private final ComponentName mComponent1 = new ComponentName("a", "b");
private final ComponentName mComponent2 = new ComponentName("b", "b");
private Context mTargetContext;
private InvariantDeviceProfile mIdp;
private LauncherAppState mAppState;
private LauncherModelHelper mModelHelper;
private IntArray mExistingScreens;
private IntArray mNewScreens;
private IntSparseArrayMap<GridOccupancy> mScreenOccupancy;
@Before
public void setup() {
mModelHelper = new LauncherModelHelper();
mTargetContext = mModelHelper.sandboxContext;
mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
mIdp.numColumns = mIdp.numRows = 5;
mAppState = LauncherAppState.getInstance(mTargetContext);
mExistingScreens = new IntArray();
mScreenOccupancy = new IntSparseArrayMap<>();
mNewScreens = new IntArray();
}
@After
public void tearDown() {
mModelHelper.destroy();
}
private AddWorkspaceItemsTask newTask(ItemInfo... items) {
List<Pair<ItemInfo, Object>> list = new ArrayList<>();
for (ItemInfo item : items) {
list.add(Pair.create(item, null));
}
return new AddWorkspaceItemsTask(list);
}
@Test
public void testFindSpaceForItem_prefers_second() throws Exception {
// First screen has only one hole of size 1
int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
// Second screen has 2 holes of sizes 3x2 and 2x3
setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
int[] spaceFound = newTask().findSpaceForItem(
mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 1, 1);
assertEquals(1, spaceFound[0]);
assertTrue(mScreenOccupancy.get(spaceFound[0])
.isRegionVacant(spaceFound[1], spaceFound[2], 1, 1));
// Find a larger space
spaceFound = newTask().findSpaceForItem(
mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 2, 3);
assertEquals(2, spaceFound[0]);
assertTrue(mScreenOccupancy.get(spaceFound[0])
.isRegionVacant(spaceFound[1], spaceFound[2], 2, 3));
}
@Test
public void testFindSpaceForItem_adds_new_screen() throws Exception {
// First screen has 2 holes of sizes 3x2 and 2x3
setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
IntArray oldScreens = mExistingScreens.clone();
int[] spaceFound = newTask().findSpaceForItem(
mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 3, 3);
assertFalse(oldScreens.contains(spaceFound[0]));
assertTrue(mNewScreens.contains(spaceFound[0]));
}
@Test
public void testAddItem_existing_item_ignored() throws Exception {
WorkspaceItemInfo info = new WorkspaceItemInfo();
info.intent = new Intent().setComponent(mComponent1);
// Setup a screen with a hole
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
// Nothing was added
assertTrue(mModelHelper.executeTaskForTest(newTask(info)).isEmpty());
}
@Test
public void testAddItem_some_items_added() throws Exception {
Callbacks callbacks = mock(Callbacks.class);
Executors.MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(callbacks)).get();
WorkspaceItemInfo info = new WorkspaceItemInfo();
info.intent = new Intent().setComponent(mComponent1);
WorkspaceItemInfo info2 = new WorkspaceItemInfo();
info2.intent = new Intent().setComponent(mComponent2);
// Setup a screen with a hole
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
mModelHelper.executeTaskForTest(newTask(info, info2)).get(0).run();
ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class);
ArgumentCaptor<ArrayList> animated = ArgumentCaptor.forClass(ArrayList.class);
// only info2 should be added because info was already added to the workspace
// in setupWorkspaceWithHoles()
verify(callbacks).bindAppsAdded(any(IntArray.class), notAnimated.capture(),
animated.capture());
assertTrue(notAnimated.getValue().isEmpty());
assertEquals(1, animated.getValue().size());
assertTrue(animated.getValue().contains(info2));
}
private int setupWorkspaceWithHoles(int startId, int screenId, Rect... holes) throws Exception {
return mModelHelper.executeSimpleTask(
model -> writeWorkspaceWithHoles(model, startId, screenId, holes));
}
private int writeWorkspaceWithHoles(
BgDataModel bgDataModel, int startId, int screenId, Rect... holes) {
GridOccupancy occupancy = new GridOccupancy(mIdp.numColumns, mIdp.numRows);
occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true);
for (Rect r : holes) {
occupancy.markCells(r, false);
}
mExistingScreens.add(screenId);
mScreenOccupancy.append(screenId, occupancy);
for (int x = 0; x < mIdp.numColumns; x++) {
for (int y = 0; y < mIdp.numRows; y++) {
if (!occupancy.cells[x][y]) {
continue;
}
WorkspaceItemInfo info = new WorkspaceItemInfo();
info.intent = new Intent().setComponent(mComponent1);
info.id = startId++;
info.screenId = screenId;
info.cellX = x;
info.cellY = y;
info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
bgDataModel.addItem(mTargetContext, info, false);
ContentWriter writer = new ContentWriter(mTargetContext);
info.writeToValues(writer);
writer.put(Favorites._ID, info.id);
mTargetContext.getContentResolver().insert(Favorites.CONTENT_URI,
writer.getValues(mTargetContext));
}
}
return startId;
}
}

View File

@ -1,325 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.model
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.util.Pair
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.*
import com.android.launcher3.util.IntArray
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.*
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.verify
import kotlin.collections.ArrayList
/**
* Tests for [AddWorkspaceItemsTask]
*/
@SmallTest
@RunWith(AndroidJUnit4::class)
class AddWorkspaceItemsTaskTest {
@Captor
private lateinit var animatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
@Captor
private lateinit var notAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
@Mock
private lateinit var dataModelCallbacks: BgDataModel.Callbacks
private lateinit var mTargetContext: Context
private lateinit var mIdp: InvariantDeviceProfile
private lateinit var mAppState: LauncherAppState
private lateinit var mModelHelper: LauncherModelHelper
private lateinit var mExistingScreens: IntArray
private lateinit var mNewScreens: IntArray
private lateinit var mScreenOccupancy: IntSparseArrayMap<GridOccupancy>
private val emptyScreenHoles = listOf(Rect(0, 0, 5, 5))
private val fullScreenHoles = emptyList<Rect>()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
mModelHelper = LauncherModelHelper()
mTargetContext = mModelHelper.sandboxContext
mIdp = InvariantDeviceProfile.INSTANCE[mTargetContext]
mIdp.numRows = 5
mIdp.numColumns = mIdp.numRows
mAppState = LauncherAppState.getInstance(mTargetContext)
mExistingScreens = IntArray()
mScreenOccupancy = IntSparseArrayMap()
mNewScreens = IntArray()
Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(dataModelCallbacks) }.get()
}
@After
fun tearDown() {
mModelHelper.destroy()
}
@Test
fun justEnoughSpaceOnFirstScreen_whenFindSpaceForItem_thenReturnFirstScreenId() {
setupWorkspacesWithHoles(
screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 hole
// 2 holes of sizes 3x2 and 2x3
screen2 = listOf(Rect(2, 0, 5, 2), Rect(0, 2, 2, 5)),
)
val spaceFound = newTask().findSpaceForItem(
mAppState, mModelHelper.bgDataModel, mExistingScreens, mNewScreens, 1, 1)
assertEquals(1, spaceFound[0])
assertTrue(mScreenOccupancy[spaceFound[0]]
.isRegionVacant(spaceFound[1], spaceFound[2], 1, 1))
}
@Test
fun notEnoughSpaceOnFirstScreen_whenFindSpaceForItem_thenReturnSecondScreenId() {
setupWorkspacesWithHoles(
screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 hole
// 2 holes of sizes 3x2 and 2x3
screen2 = listOf(Rect(2, 0, 5, 2), Rect(0, 2, 2, 5)),
)
// Find a larger space
val spaceFound = newTask().findSpaceForItem(
mAppState, mModelHelper.bgDataModel, mExistingScreens, mNewScreens, 2, 3)
assertEquals(2, spaceFound[0])
assertTrue(mScreenOccupancy[spaceFound[0]]
.isRegionVacant(spaceFound[1], spaceFound[2], 2, 3))
}
@Test
fun notEnoughSpaceOnExistingScreens_whenFindSpaceForItem_thenReturnNewScreenId() {
setupWorkspacesWithHoles(
// 2 holes of sizes 3x2 and 2x3
screen1 = listOf(Rect(2, 0, 5, 2), Rect(0, 2, 2, 5)),
// 2 holes of sizes 1x2 and 2x2
screen2 = listOf(Rect(1, 0, 2, 2), Rect(3, 2, 5, 4)),
)
val oldScreens = mExistingScreens.clone()
val spaceFound = newTask().findSpaceForItem(
mAppState, mModelHelper.bgDataModel, mExistingScreens, mNewScreens, 3, 3)
assertFalse(oldScreens.contains(spaceFound[0]))
assertTrue(mNewScreens.contains(spaceFound[0]))
}
@Test
fun enoughSpaceOnFirstScreen_whenTaskRuns_thenAddItemToFirstScreen() {
val workspaceHoles = createWorkspaceHoles(
screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 space
screen2 = listOf(Rect(2, 0, 5, 2)), // 3x2 space
)
val addedItems = testAddItems(workspaceHoles, getNewItem())
assertEquals(1, addedItems.size)
assertEquals(1, addedItems.first().itemInfo.screenId)
}
@Test
fun firstPageIsFull_whenTaskRuns_thenAddItemToSecondScreen() {
val workspaceHoles = createWorkspaceHoles(
screen1 = fullScreenHoles,
)
val addedItems = testAddItems(workspaceHoles, getNewItem())
assertEquals(1, addedItems.size)
assertEquals(2, addedItems.first().itemInfo.screenId)
}
@Test
fun firstScreenIsEmptyButSecondIsNotEmpty_whenTaskRuns_thenAddItemToSecondScreen() {
val workspaceHoles = createWorkspaceHoles(
screen1 = emptyScreenHoles,
screen2 = listOf(Rect(2, 0, 5, 2)), // 3x2 space
)
val addedItems = testAddItems(workspaceHoles, getNewItem())
assertEquals(1, addedItems.size)
assertEquals(2, addedItems.first().itemInfo.screenId)
}
@Test
fun twoEmptyMiddleScreens_whenTaskRuns_thenAddItemToThirdScreen() {
val workspaceHoles = createWorkspaceHoles(
screen1 = emptyScreenHoles,
screen2 = emptyScreenHoles,
screen3 = listOf(Rect(1, 1, 4, 4)), // 3x3 space
)
val addedItems = testAddItems(workspaceHoles, getNewItem())
assertEquals(1, addedItems.size)
assertEquals(3, addedItems.first().itemInfo.screenId)
}
@Test
fun allPagesAreFull_whenTaskRuns_thenAddItemToNewScreen() {
val workspaceHoles = createWorkspaceHoles(
screen1 = fullScreenHoles,
screen2 = fullScreenHoles,
)
val addedItems = testAddItems(workspaceHoles, getNewItem())
assertEquals(1, addedItems.size)
assertEquals(3, addedItems.first().itemInfo.screenId)
}
@Test
fun firstTwoPagesAreFull_and_ThirdPageIsEmpty_whenTaskRuns_thenAddItemToThirdPage() {
val workspaceHoles = createWorkspaceHoles(
screen1 = fullScreenHoles,
screen2 = fullScreenHoles,
screen3 = emptyScreenHoles
)
val addedItems = testAddItems(workspaceHoles, getNewItem())
assertEquals(1, addedItems.size)
assertEquals(3, addedItems.first().itemInfo.screenId)
}
@Test
fun itemIsAlreadyAdded_whenTaskRun_thenIgnoreItem() {
val task = newTask(getExistingItem())
setupWorkspacesWithHoles(
screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 hole
)
// Nothing was added
assertTrue(mModelHelper.executeTaskForTest(task).isEmpty())
}
@Test
fun newAndExistingItems_whenTaskRun_thenAddOnlyTheNewOne() {
val newItem = getNewItem()
val workspaceHoles = createWorkspaceHoles(
screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 hole
)
val addedItems = testAddItems(workspaceHoles, getExistingItem(), newItem)
assertEquals(1, addedItems.size)
val addedItem = addedItems.first()
assert(addedItem.isAnimated)
val addedItemInfo = addedItem.itemInfo
assertEquals(1, addedItemInfo.screenId)
assertEquals(newItem, addedItemInfo)
}
private fun testAddItems(
workspaceHoles: List<List<Rect>>,
vararg itemsToAdd: WorkspaceItemInfo
): List<AddedItem> {
setupWorkspaces(workspaceHoles)
mModelHelper.executeTaskForTest(newTask(*itemsToAdd))[0].run()
verify(dataModelCallbacks).bindAppsAdded(any(),
notAnimatedItemArgumentCaptor.capture(), animatedItemArgumentCaptor.capture())
val addedItems = mutableListOf<AddedItem>()
addedItems.addAll(animatedItemArgumentCaptor.value.map { AddedItem(it, true) })
addedItems.addAll(notAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) })
return addedItems
}
private fun setupWorkspaces(workspaceHoles: List<List<Rect>>) {
var nextItemId = 1
var screenId = 1
workspaceHoles.forEach { holes ->
nextItemId = setupWorkspace(nextItemId, screenId++, *holes.toTypedArray())
}
}
private fun setupWorkspace(startId: Int, screenId: Int, vararg holes: Rect): Int {
return mModelHelper.executeSimpleTask { dataModel ->
writeWorkspaceWithHoles(dataModel, startId, screenId, *holes)
}
}
private fun writeWorkspaceWithHoles(
bgDataModel: BgDataModel,
itemStartId: Int,
screenId: Int,
vararg holes: Rect,
): Int {
var itemId = itemStartId
val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows)
occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true)
holes.forEach { holeRect ->
occupancy.markCells(holeRect, false)
}
mExistingScreens.add(screenId)
mScreenOccupancy.append(screenId, occupancy)
for (x in 0 until mIdp.numColumns) {
for (y in 0 until mIdp.numRows) {
if (!occupancy.cells[x][y]) {
continue
}
val info = getExistingItem()
info.id = itemId++
info.screenId = screenId
info.cellX = x
info.cellY = y
info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP
bgDataModel.addItem(mTargetContext, info, false)
val writer = ContentWriter(mTargetContext)
info.writeToValues(writer)
writer.put(LauncherSettings.Favorites._ID, info.id)
mTargetContext.contentResolver.insert(LauncherSettings.Favorites.CONTENT_URI,
writer.getValues(mTargetContext))
}
}
return itemId
}
private fun setupWorkspacesWithHoles(
screen1: List<Rect>? = null,
screen2: List<Rect>? = null,
screen3: List<Rect>? = null,
) = createWorkspaceHoles(screen1, screen2, screen3)
.let(this::setupWorkspaces)
private fun createWorkspaceHoles(
screen1: List<Rect>? = null,
screen2: List<Rect>? = null,
screen3: List<Rect>? = null,
): List<List<Rect>> = listOfNotNull(screen1, screen2, screen3)
private fun newTask(vararg items: ItemInfo): AddWorkspaceItemsTask =
items.map { Pair.create(it, Any()) }
.toMutableList()
.let(::AddWorkspaceItemsTask)
private fun getExistingItem() = WorkspaceItemInfo()
.apply { intent = Intent().setComponent(ComponentName("a", "b")) }
private fun getNewItem() = WorkspaceItemInfo()
.apply { intent = Intent().setComponent(ComponentName("b", "b")) }
}
private data class AddedItem(
val itemInfo: ItemInfo,
val isAnimated: Boolean
)