summaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/launcher3/model
diff options
context:
space:
mode:
authorHyunyoung Song <hyunyoungs@google.com>2017-05-10 13:53:50 -0700
committerHyunyoung Song <hyunyoungs@google.com>2017-05-10 16:52:47 -0700
commit311ec8962bef83bc240809e8365722a3a57dfec0 (patch)
tree001a6636ce1d55bd2e31ea69aa4ed2202591795d /tests/src/com/android/launcher3/model
parent090ce68ceecc09780db4797277a3aaf8c7546a0b (diff)
parent5cfde85cb6d095585b932d9e3e2c4fe78aa0dafe (diff)
downloadandroid_packages_apps_Trebuchet-311ec8962bef83bc240809e8365722a3a57dfec0.tar.gz
android_packages_apps_Trebuchet-311ec8962bef83bc240809e8365722a3a57dfec0.tar.bz2
android_packages_apps_Trebuchet-311ec8962bef83bc240809e8365722a3a57dfec0.zip
merged ub-launcher3-dorval, and resolved conflicts
Bug: 36904684 Bug: 37929893 Bug: 36068989 Test: make -j 32 dist checkbuild Change-Id: If9b11b212852cb1048d54db2224dab4acf2d93e0
Diffstat (limited to 'tests/src/com/android/launcher3/model')
-rw-r--r--tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java183
-rw-r--r--tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java226
-rw-r--r--tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java82
-rw-r--r--tests/src/com/android/launcher3/model/LoaderCursorTest.java236
-rw-r--r--tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java61
5 files changed, 788 insertions, 0 deletions
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
new file mode 100644
index 000000000..d0ba9074c
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -0,0 +1,183 @@
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.util.Pair;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.Provider;
+
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link AddWorkspaceItemsTask}
+ */
+public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
+
+ private final ComponentName mComponent1 = new ComponentName("a", "b");
+ private final ComponentName mComponent2 = new ComponentName("b", "b");
+
+ private ArrayList<Long> existingScreens;
+ private ArrayList<Long> newScreens;
+ private LongArrayMap<GridOccupancy> screenOccupancy;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ existingScreens = new ArrayList<>();
+ screenOccupancy = new LongArrayMap<>();
+ newScreens = new ArrayList<>();
+
+ idp.numColumns = 5;
+ idp.numRows = 5;
+ }
+
+ private AddWorkspaceItemsTask newTask(ItemInfo... items) {
+ return new AddWorkspaceItemsTask(Provider.of(Arrays.asList(items))) {
+
+ @Override
+ protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { }
+ };
+ }
+
+ public void testFindSpaceForItem_prefers_second() {
+ // 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));
+
+ Pair<Long, int[]> spaceFound = newTask()
+ .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 1, 1);
+ assertEquals(2L, (long) spaceFound.first);
+ assertTrue(screenOccupancy.get(spaceFound.first)
+ .isRegionVacant(spaceFound.second[0], spaceFound.second[1], 1, 1));
+
+ // Find a larger space
+ spaceFound = newTask()
+ .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 2, 3);
+ assertEquals(2L, (long) spaceFound.first);
+ assertTrue(screenOccupancy.get(spaceFound.first)
+ .isRegionVacant(spaceFound.second[0], spaceFound.second[1], 2, 3));
+ }
+
+ 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));
+ commitScreensToDb();
+
+ when(appState.getContext()).thenReturn(getMockContext());
+
+ ArrayList<Long> oldScreens = new ArrayList<>(existingScreens);
+ Pair<Long, int[]> spaceFound = newTask()
+ .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 3, 3);
+ assertFalse(oldScreens.contains(spaceFound.first));
+ assertTrue(newScreens.contains(spaceFound.first));
+ }
+
+ public void testAddItem_existing_item_ignored() throws Exception {
+ ShortcutInfo info = new ShortcutInfo();
+ info.intent = new Intent().setComponent(mComponent1);
+
+ // Setup a screen with a hole
+ setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+ commitScreensToDb();
+
+ when(appState.getContext()).thenReturn(getMockContext());
+
+ // Nothing was added
+ assertTrue(executeTaskForTest(newTask(info)).isEmpty());
+ }
+
+ public void testAddItem_some_items_added() throws Exception {
+ ShortcutInfo info = new ShortcutInfo();
+ info.intent = new Intent().setComponent(mComponent1);
+
+ ShortcutInfo info2 = new ShortcutInfo();
+ info2.intent = new Intent().setComponent(mComponent2);
+
+ // Setup a screen with a hole
+ setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+ commitScreensToDb();
+
+ when(appState.getContext()).thenReturn(getMockContext());
+
+ 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(ArrayList.class), notAnimated.capture(),
+ animated.capture(), any(ArrayList.class));
+ assertTrue(notAnimated.getValue().isEmpty());
+
+ assertEquals(1, animated.getValue().size());
+ assertTrue(animated.getValue().contains(info2));
+ }
+
+ private int setupWorkspaceWithHoles(int startId, long screenId, Rect... holes) {
+ GridOccupancy occupancy = new GridOccupancy(idp.numColumns, idp.numRows);
+ occupancy.markCells(0, 0, idp.numColumns, idp.numRows, true);
+ for (Rect r : holes) {
+ occupancy.markCells(r, false);
+ }
+
+ existingScreens.add(screenId);
+ screenOccupancy.append(screenId, occupancy);
+
+ for (int x = 0; x < idp.numColumns; x++) {
+ for (int y = 0; y < idp.numRows; y++) {
+ if (!occupancy.cells[x][y]) {
+ continue;
+ }
+
+ ShortcutInfo info = new ShortcutInfo();
+ 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(targetContext, info, false);
+ }
+ }
+ return startId;
+ }
+
+ private void commitScreensToDb() throws Exception {
+ LauncherSettings.Settings.call(getMockContentResolver(),
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+
+ Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
+ ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+ // Clear the table
+ ops.add(ContentProviderOperation.newDelete(uri).build());
+ int count = existingScreens.size();
+ for (int i = 0; i < count; i++) {
+ ContentValues v = new ContentValues();
+ long screenId = existingScreens.get(i);
+ v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
+ v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
+ ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
+ }
+ getMockContentResolver().applyBatch(ProviderConfig.AUTHORITY, ops);
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
new file mode 100644
index 000000000..b9944db98
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -0,0 +1,226 @@
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.os.Process;
+import android.os.UserHandle;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.test.ProviderTestCase2;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppFilter;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.DeferredHandler;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.BaseModelUpdateTask;
+import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Provider;
+import com.android.launcher3.util.TestLauncherProvider;
+
+import org.mockito.ArgumentCaptor;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Base class for writing tests for Model update tasks.
+ */
+public class BaseModelUpdateTaskTestCase extends ProviderTestCase2<TestLauncherProvider> {
+
+ public final HashMap<Class, HashMap<String, Field>> fieldCache = new HashMap<>();
+
+ public Context targetContext;
+ public UserHandle myUser;
+
+ public InvariantDeviceProfile idp;
+ public LauncherAppState appState;
+ public LauncherModel model;
+ public ModelWriter modelWriter;
+ public MyIconCache iconCache;
+
+ public BgDataModel bgDataModel;
+ public AllAppsList allAppsList;
+ public Callbacks callbacks;
+
+ public BaseModelUpdateTaskTestCase() {
+ super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ callbacks = mock(Callbacks.class);
+ appState = mock(LauncherAppState.class);
+ model = mock(LauncherModel.class);
+ modelWriter = mock(ModelWriter.class);
+ when(appState.getModel()).thenReturn(model);
+ when(model.getWriter(anyBoolean())).thenReturn(modelWriter);
+
+ myUser = Process.myUserHandle();
+
+ bgDataModel = new BgDataModel();
+ targetContext = InstrumentationRegistry.getTargetContext();
+ idp = new InvariantDeviceProfile();
+ iconCache = new MyIconCache(targetContext, idp);
+
+ allAppsList = new AllAppsList(iconCache, new AppFilter());
+
+ when(appState.getIconCache()).thenReturn(iconCache);
+ when(appState.getInvariantDeviceProfile()).thenReturn(idp);
+ }
+
+ /**
+ * Synchronously executes the task and returns all the UI callbacks posted.
+ */
+ public List<Runnable> executeTaskForTest(BaseModelUpdateTask task) throws Exception {
+ LauncherModel mockModel = mock(LauncherModel.class);
+ when(mockModel.getCallback()).thenReturn(callbacks);
+
+ Field f = BaseModelUpdateTask.class.getDeclaredField("mModel");
+ f.setAccessible(true);
+ f.set(task, mockModel);
+
+ DeferredHandler mockHandler = mock(DeferredHandler.class);
+ f = BaseModelUpdateTask.class.getDeclaredField("mUiHandler");
+ f.setAccessible(true);
+ f.set(task, mockHandler);
+
+ task.execute(appState, bgDataModel, allAppsList);
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mockHandler, atLeast(0)).post(captor.capture());
+
+ return captor.getAllValues();
+ }
+
+ /**
+ * Initializes mock data for the test.
+ */
+ public void initializeData(String resourceName) throws Exception {
+ Context myContext = InstrumentationRegistry.getContext();
+ Resources res = myContext.getResources();
+ int id = res.getIdentifier(resourceName, "raw", myContext.getPackageName());
+ try (BufferedReader reader =
+ new BufferedReader(new InputStreamReader(res.openRawResource(id)))) {
+ String line;
+ HashMap<String, Class> classMap = new HashMap<>();
+ while((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.startsWith("#") || line.isEmpty()) {
+ continue;
+ }
+ String[] commands = line.split(" ");
+ switch (commands[0]) {
+ case "classMap":
+ classMap.put(commands[1], Class.forName(commands[2]));
+ break;
+ case "bgItem":
+ bgDataModel.addItem(targetContext,
+ (ItemInfo) initItem(classMap.get(commands[1]), commands, 2), false);
+ break;
+ case "allApps":
+ allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
+ break;
+ }
+ }
+ }
+ }
+
+ private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception {
+ HashMap<String, Field> cache = fieldCache.get(clazz);
+ if (cache == null) {
+ cache = new HashMap<>();
+ Class c = clazz;
+ while (c != null) {
+ for (Field f : c.getDeclaredFields()) {
+ f.setAccessible(true);
+ cache.put(f.getName(), f);
+ }
+ c = c.getSuperclass();
+ }
+ fieldCache.put(clazz, cache);
+ }
+
+ Object item = clazz.newInstance();
+ for (int i = startIndex; i < fieldDef.length; i++) {
+ String[] fieldData = fieldDef[i].split("=", 2);
+ Field f = cache.get(fieldData[0]);
+ Class type = f.getType();
+ if (type == int.class || type == long.class) {
+ f.set(item, Integer.parseInt(fieldData[1]));
+ } else if (type == CharSequence.class || type == String.class) {
+ f.set(item, fieldData[1]);
+ } else if (type == Intent.class) {
+ if (!fieldData[1].startsWith("#Intent")) {
+ fieldData[1] = "#Intent;" + fieldData[1] + ";end";
+ }
+ f.set(item, Intent.parseUri(fieldData[1], 0));
+ } else if (type == ComponentName.class) {
+ f.set(item, ComponentName.unflattenFromString(fieldData[1]));
+ } else {
+ throw new Exception("Added parsing logic for "
+ + f.getName() + " of type " + f.getType());
+ }
+ }
+ return item;
+ }
+
+ public static class MyIconCache extends IconCache {
+
+ private final HashMap<ComponentKey, CacheEntry> mCache = new HashMap<>();
+
+ public MyIconCache(Context context, InvariantDeviceProfile idp) {
+ super(context, idp);
+ }
+
+ @Override
+ protected CacheEntry cacheLocked(
+ @NonNull ComponentName componentName,
+ @NonNull Provider<LauncherActivityInfo> infoProvider,
+ UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
+ CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
+ if (entry == null) {
+ entry = new CacheEntry();
+ entry.icon = getDefaultIcon(user);
+ }
+ return entry;
+ }
+
+ public void addCache(ComponentName key, String title) {
+ CacheEntry entry = new CacheEntry();
+ entry.icon = newIcon();
+ entry.title = title;
+ mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
+ }
+
+ public Bitmap newIcon() {
+ return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
+ }
+
+ @Override
+ protected Bitmap makeDefaultIcon(UserHandle user) {
+ return newIcon();
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
new file mode 100644
index 000000000..d595e6cf1
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -0,0 +1,82 @@
+package com.android.launcher3.model;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ShortcutInfo;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * Tests for {@link CacheDataUpdatedTask}
+ */
+public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
+
+ private static final String NEW_LABEL_PREFIX = "new-label-";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ initializeData("cache_data_updated_task_data");
+ // Add dummy entries in the cache to simulate update
+ for (ItemInfo info : bgDataModel.itemsIdMap) {
+ iconCache.addCache(info.getTargetComponent(), NEW_LABEL_PREFIX + info.id);
+ }
+ }
+
+ private CacheDataUpdatedTask newTask(int op, String... pkg) {
+ return new CacheDataUpdatedTask(op, myUser, new HashSet<>(Arrays.asList(pkg)));
+ }
+
+ public void testCacheUpdate_update_apps() throws Exception {
+ // Clear all icons from apps list so that its easy to check what was updated
+ for (AppInfo info : allAppsList.data) {
+ info.iconBitmap = null;
+ }
+
+ executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
+
+ // Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
+ // is not updated
+ verifyUpdate(1L, 2L);
+
+ // Verify that only app1 var updated in allAppsList
+ assertFalse(allAppsList.data.isEmpty());
+ for (AppInfo info : allAppsList.data) {
+ if (info.componentName.getPackageName().equals("app1")) {
+ assertNotNull(info.iconBitmap);
+ } else {
+ assertNull(info.iconBitmap);
+ }
+ }
+ }
+
+ public void testSessionUpdate_ignores_normal_apps() throws Exception {
+ executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
+
+ // app1 has no restored shortcuts. Verify that nothing was updated.
+ verifyUpdate();
+ }
+
+ public void testSessionUpdate_updates_pending_apps() throws Exception {
+ executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
+
+ // app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were
+ // were updated
+ verifyUpdate(5L, 6L);
+ }
+
+ private void verifyUpdate(Long... idsUpdated) {
+ HashSet<Long> updates = new HashSet<>(Arrays.asList(idsUpdated));
+ for (ItemInfo info : bgDataModel.itemsIdMap) {
+ if (updates.contains(info.id)) {
+ assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
+ assertNotNull(((ShortcutInfo) info).iconBitmap);
+ } else {
+ assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
+ assertNull(((ShortcutInfo) info).iconBitmap);
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
new file mode 100644
index 000000000..173c55621
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -0,0 +1,236 @@
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.os.Process;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherAppsCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static com.android.launcher3.LauncherSettings.BaseLauncherColumns.INTENT;
+import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
+import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON_PACKAGE;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON_RESOURCE;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
+import static com.android.launcher3.LauncherSettings.Favorites.RESTORED;
+import static com.android.launcher3.LauncherSettings.Favorites.SCREEN;
+import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
+import static com.android.launcher3.LauncherSettings.Favorites._ID;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link LoaderCursor}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LoaderCursorTest {
+
+ private LauncherAppState mMockApp;
+ private IconCache mMockIconCache;
+
+ private MatrixCursor mCursor;
+ private InvariantDeviceProfile mIDP;
+ private Context mContext;
+ private LauncherAppsCompat mLauncherApps;
+
+ private LoaderCursor mLoaderCursor;
+
+ @Before
+ public void setup() {
+ mIDP = new InvariantDeviceProfile();
+ mCursor = new MatrixCursor(new String[] {
+ ICON, ICON_PACKAGE, ICON_RESOURCE, TITLE,
+ _ID, CONTAINER, ITEM_TYPE, PROFILE_ID,
+ SCREEN, CELLX, CELLY, RESTORED, INTENT
+ });
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ mMockApp = mock(LauncherAppState.class);
+ mMockIconCache = mock(IconCache.class);
+ when(mMockApp.getIconCache()).thenReturn(mMockIconCache);
+ when(mMockApp.getInvariantDeviceProfile()).thenReturn(mIDP);
+ when(mMockApp.getContext()).thenReturn(mContext);
+ mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+
+ mLoaderCursor = new LoaderCursor(mCursor, mMockApp);
+ mLoaderCursor.allUsers.put(0, Process.myUserHandle());
+ }
+
+ private void initCursor(int itemType, String title) {
+ mCursor.newRow()
+ .add(_ID, 1)
+ .add(PROFILE_ID, 0)
+ .add(ITEM_TYPE, itemType)
+ .add(TITLE, title)
+ .add(CONTAINER, CONTAINER_DESKTOP);
+ }
+
+ @Test
+ public void getAppShortcutInfo_dontAllowMissing_invalidComponent() {
+ initCursor(ITEM_TYPE_APPLICATION, "");
+ assertTrue(mLoaderCursor.moveToNext());
+ ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+ assertNull(mLoaderCursor.getAppShortcutInfo(
+ new Intent().setComponent(cn), false /* allowMissingTarget */, true));
+ }
+
+ @Test
+ public void getAppShortcutInfo_dontAllowMissing_validComponent() {
+ initCursor(ITEM_TYPE_APPLICATION, "");
+ assertTrue(mLoaderCursor.moveToNext());
+
+ ComponentName cn = mLauncherApps.getActivityList(null, mLoaderCursor.user)
+ .get(0).getComponentName();
+ ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+ new Intent().setComponent(cn), false /* allowMissingTarget */, true);
+ assertNotNull(info);
+ assertTrue(Utilities.isLauncherAppTarget(info.intent));
+ }
+
+ @Test
+ public void getAppShortcutInfo_allowMissing_invalidComponent() {
+ initCursor(ITEM_TYPE_APPLICATION, "");
+ assertTrue(mLoaderCursor.moveToNext());
+
+ ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+ ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+ new Intent().setComponent(cn), true /* allowMissingTarget */, true);
+ assertNotNull(info);
+ assertTrue(Utilities.isLauncherAppTarget(info.intent));
+ }
+
+ @Test
+ public void loadSimpleShortcut() {
+ initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut");
+ assertTrue(mLoaderCursor.moveToNext());
+
+ Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+ when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user))).thenReturn(icon);
+ ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
+ assertEquals(icon, info.iconBitmap);
+ assertEquals("my-shortcut", info.title);
+ assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
+ }
+
+ @Test
+ public void checkItemPlacement_wrongWorkspaceScreen() {
+ ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 3L));
+ mIDP.numRows = 4;
+ mIDP.numColumns = 4;
+ mIDP.numHotseatIcons = 3;
+
+ // Item on unknown screen are not placed
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 4L), workspaceScreens));
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 5L), workspaceScreens));
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 3L), workspaceScreens));
+
+ }
+ @Test
+ public void checkItemPlacement_outsideBounds() {
+ ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 2L));
+ mIDP.numRows = 4;
+ mIDP.numColumns = 4;
+ mIDP.numHotseatIcons = 3;
+
+ // Item outside screen bounds are not placed
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ }
+
+ @Test
+ public void checkItemPlacement_overlappingItems() {
+ ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 2L));
+ mIDP.numRows = 4;
+ mIDP.numColumns = 4;
+ mIDP.numHotseatIcons = 3;
+
+ // Overlapping items are not placed
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1L), workspaceScreens));
+
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1L), workspaceScreens));
+ }
+
+ @Test
+ public void checkItemPlacement_hotseat() {
+ ArrayList<Long> workspaceScreens = new ArrayList<>();
+ mIDP.numRows = 4;
+ mIDP.numColumns = 4;
+ mIDP.numHotseatIcons = 3;
+
+ // Hotseat items are only placed based on screenId
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1L), workspaceScreens));
+ assertTrue(mLoaderCursor.checkItemPlacement(
+ newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2L), workspaceScreens));
+
+ assertFalse(mLoaderCursor.checkItemPlacement(
+ newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3L), workspaceScreens));
+ }
+
+ private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
+ long container, long screenId) {
+ ItemInfo info = new ItemInfo();
+ info.cellX = cellX;
+ info.cellY = cellY;
+ info.spanX = spanX;
+ info.spanY = spanY;
+ info.container = container;
+ info.screenId = screenId;
+ return info;
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
new file mode 100644
index 000000000..d6555620c
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -0,0 +1,61 @@
+package com.android.launcher3.model;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * Tests for {@link PackageInstallStateChangedTask}
+ */
+public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ initializeData("package_install_state_change_task_data");
+ }
+
+ private PackageInstallStateChangedTask newTask(String pkg, int progress) {
+ PackageInstallInfo installInfo = new PackageInstallInfo(pkg);
+ installInfo.progress = progress;
+ installInfo.state = PackageInstallerCompat.STATUS_INSTALLING;
+ return new PackageInstallStateChangedTask(installInfo);
+ }
+
+ public void testSessionUpdate_ignore_installed() throws Exception {
+ executeTaskForTest(newTask("app1", 30));
+
+ // No shortcuts were updated
+ verifyProgressUpdate(0);
+ }
+
+ public void testSessionUpdate_shortcuts_updated() throws Exception {
+ executeTaskForTest(newTask("app3", 30));
+
+ verifyProgressUpdate(30, 5L, 6L, 7L);
+ }
+
+ public void testSessionUpdate_widgets_updated() throws Exception {
+ executeTaskForTest(newTask("app4", 30));
+
+ verifyProgressUpdate(30, 8L, 9L);
+ }
+
+ private void verifyProgressUpdate(int progress, Long... idsUpdated) {
+ HashSet<Long> updates = new HashSet<>(Arrays.asList(idsUpdated));
+ for (ItemInfo info : bgDataModel.itemsIdMap) {
+ if (info instanceof ShortcutInfo) {
+ assertEquals(updates.contains(info.id) ? progress: 0,
+ ((ShortcutInfo) info).getInstallProgress());
+ } else {
+ assertEquals(updates.contains(info.id) ? progress: -1,
+ ((LauncherAppWidgetInfo) info).installProgress);
+ }
+ }
+ }
+}