Updating Robolectric tests
> Adding multi-thread support > Simulating actual loader loading flow > Moving some android tests to robolectic Change-Id: Ie17a448f20e8a4b1f18ecc33d22054bbf9e18729
This commit is contained in:
parent
993aef828a
commit
9ae9b60043
|
@ -45,6 +45,7 @@ import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.launcher3.icons.BaseIconFactory;
|
import com.android.launcher3.icons.BaseIconFactory;
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
|
@ -250,9 +251,9 @@ public abstract class BaseIconCache {
|
||||||
* @param replaceExisting if true, it will recreate the bitmap even if it already exists in
|
* @param replaceExisting if true, it will recreate the bitmap even if it already exists in
|
||||||
* the memory. This is useful then the previous bitmap was created using
|
* the memory. This is useful then the previous bitmap was created using
|
||||||
* old data.
|
* old data.
|
||||||
* package private
|
|
||||||
*/
|
*/
|
||||||
protected synchronized <T> void addIconToDBAndMemCache(T object, CachingLogic<T> cachingLogic,
|
@VisibleForTesting
|
||||||
|
public synchronized <T> void addIconToDBAndMemCache(T object, CachingLogic<T> cachingLogic,
|
||||||
PackageInfo info, long userSerial, boolean replaceExisting) {
|
PackageInfo info, long userSerial, boolean replaceExisting) {
|
||||||
UserHandle user = cachingLogic.getUser(object);
|
UserHandle user = cachingLogic.getUser(object);
|
||||||
ComponentName componentName = cachingLogic.getComponent(object);
|
ComponentName componentName = cachingLogic.getComponent(object);
|
||||||
|
|
|
@ -19,6 +19,8 @@ LOCAL_PATH := $(call my-dir)
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_MODULE := LauncherRoboTests
|
LOCAL_MODULE := LauncherRoboTests
|
||||||
|
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
|
||||||
|
|
||||||
LOCAL_SDK_VERSION := current
|
LOCAL_SDK_VERSION := current
|
||||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||||
|
@ -34,6 +36,9 @@ LOCAL_JAVA_RESOURCE_DIRS := resources config
|
||||||
LOCAL_INSTRUMENTATION_FOR := Launcher3
|
LOCAL_INSTRUMENTATION_FOR := Launcher3
|
||||||
LOCAL_MODULE_TAGS := optional
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
|
# Generate test_config.properties
|
||||||
|
include external/robolectric-shadows/gen_test_config.mk
|
||||||
|
|
||||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
|
@ -43,14 +48,11 @@ include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_MODULE := RunLauncherRoboTests
|
LOCAL_MODULE := RunLauncherRoboTests
|
||||||
LOCAL_SDK_VERSION := current
|
LOCAL_SDK_VERSION := current
|
||||||
LOCAL_JAVA_LIBRARIES := \
|
LOCAL_JAVA_LIBRARIES := LauncherRoboTests
|
||||||
LauncherRoboTests
|
|
||||||
|
|
||||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||||
|
|
||||||
LOCAL_TEST_PACKAGE := Launcher3
|
LOCAL_TEST_PACKAGE := Launcher3
|
||||||
|
LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
|
||||||
LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src \
|
|
||||||
|
|
||||||
LOCAL_ROBOTEST_TIMEOUT := 36000
|
LOCAL_ROBOTEST_TIMEOUT := 36000
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
manifest=packages/apps/Launcher3/AndroidManifest.xml
|
sdk=28
|
||||||
sdk=26
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.lang.annotation.Repeatable;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -35,6 +36,8 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
public final class FlagOverrideRule implements TestRule {
|
public final class FlagOverrideRule implements TestRule {
|
||||||
|
|
||||||
|
private final HashMap<String, Boolean> mDefaultOverrides = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container annotation for handling multiple {@link FlagOverride} annotations.
|
* Container annotation for handling multiple {@link FlagOverride} annotations.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -60,6 +63,14 @@ public final class FlagOverrideRule implements TestRule {
|
||||||
return new MyStatement(base, description);
|
return new MyStatement(base, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a default override to apply on all tests
|
||||||
|
*/
|
||||||
|
public FlagOverrideRule setOverride(BaseTogglableFlag flag, boolean value) {
|
||||||
|
mDefaultOverrides.put(flag.getKey(), value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private class MyStatement extends Statement {
|
private class MyStatement extends Statement {
|
||||||
|
|
||||||
private final Statement mBase;
|
private final Statement mBase;
|
||||||
|
@ -87,11 +98,15 @@ public final class FlagOverrideRule implements TestRule {
|
||||||
overrides = ((FlagOverrides) annotation).value();
|
overrides = ((FlagOverrides) annotation).value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (FlagOverride override : overrides) {
|
|
||||||
BaseTogglableFlag flag = allFlags.get(override.key());
|
HashMap<String, Boolean> allOverrides = new HashMap<>(mDefaultOverrides);
|
||||||
|
Arrays.stream(overrides).forEach(o -> allOverrides.put(o.key(), o.value()));
|
||||||
|
|
||||||
|
allOverrides.forEach((key, val) -> {
|
||||||
|
BaseTogglableFlag flag = allFlags.get(key);
|
||||||
changedValues.put(flag, flag.get());
|
changedValues.put(flag, flag.get());
|
||||||
flag.setForTests(override.value());
|
flag.setForTests(val);
|
||||||
}
|
});
|
||||||
mBase.evaluate();
|
mBase.evaluate();
|
||||||
} finally {
|
} finally {
|
||||||
// Clear the values
|
// Clear the values
|
||||||
|
|
|
@ -4,16 +4,16 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import com.android.launcher3.config.FlagOverrideRule.FlagOverride;
|
import com.android.launcher3.config.FlagOverrideRule.FlagOverride;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sample Robolectric test that demonstrates flag-overriding.
|
* Sample Robolectric test that demonstrates flag-overriding.
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class FlagOverrideSampleTest {
|
public class FlagOverrideSampleTest {
|
||||||
|
|
||||||
// Check out https://junit.org/junit4/javadoc/4.12/org/junit/Rule.html for more information
|
// Check out https://junit.org/junit4/javadoc/4.12/org/junit/Rule.html for more information
|
||||||
|
|
|
@ -3,11 +3,12 @@ package com.android.launcher3.logging;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.Shadows;
|
import org.robolectric.Shadows;
|
||||||
import org.robolectric.util.Scheduler;
|
import org.robolectric.util.Scheduler;
|
||||||
|
@ -20,7 +21,7 @@ import java.util.Calendar;
|
||||||
/**
|
/**
|
||||||
* Tests for {@link FileLog}
|
* Tests for {@link FileLog}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class FileLogTest {
|
public class FileLogTest {
|
||||||
|
|
||||||
private File mTempDir;
|
private File mTempDir;
|
||||||
|
|
|
@ -4,54 +4,70 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import com.android.launcher3.InvariantDeviceProfile;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfo;
|
||||||
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.LauncherSettings.Favorites;
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
import com.android.launcher3.WorkspaceItemInfo;
|
import com.android.launcher3.WorkspaceItemInfo;
|
||||||
|
import com.android.launcher3.model.BgDataModel.Callbacks;
|
||||||
import com.android.launcher3.util.ContentWriter;
|
import com.android.launcher3.util.ContentWriter;
|
||||||
import com.android.launcher3.util.GridOccupancy;
|
import com.android.launcher3.util.GridOccupancy;
|
||||||
import com.android.launcher3.util.IntArray;
|
import com.android.launcher3.util.IntArray;
|
||||||
import com.android.launcher3.util.IntSparseArrayMap;
|
import com.android.launcher3.util.IntSparseArrayMap;
|
||||||
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.LooperMode;
|
||||||
|
import org.robolectric.annotation.LooperMode.Mode;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link AddWorkspaceItemsTask}
|
* Tests for {@link AddWorkspaceItemsTask}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
@LooperMode(Mode.PAUSED)
|
||||||
|
public class AddWorkspaceItemsTaskTest {
|
||||||
|
|
||||||
private final ComponentName mComponent1 = new ComponentName("a", "b");
|
private final ComponentName mComponent1 = new ComponentName("a", "b");
|
||||||
private final ComponentName mComponent2 = new ComponentName("b", "b");
|
private final ComponentName mComponent2 = new ComponentName("b", "b");
|
||||||
|
|
||||||
private IntArray existingScreens;
|
private Context mTargetContext;
|
||||||
private IntArray newScreens;
|
private InvariantDeviceProfile mIdp;
|
||||||
private IntSparseArrayMap<GridOccupancy> screenOccupancy;
|
private LauncherAppState mAppState;
|
||||||
|
private LauncherModelHelper mModelHelper;
|
||||||
|
|
||||||
|
private IntArray mExistingScreens;
|
||||||
|
private IntArray mNewScreens;
|
||||||
|
private IntSparseArrayMap<GridOccupancy> mScreenOccupancy;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void initData() throws Exception {
|
public void setup() {
|
||||||
existingScreens = new IntArray();
|
mModelHelper = new LauncherModelHelper();
|
||||||
screenOccupancy = new IntSparseArrayMap<>();
|
mTargetContext = RuntimeEnvironment.application;
|
||||||
newScreens = new IntArray();
|
mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
|
||||||
|
mIdp.numColumns = mIdp.numRows = 5;
|
||||||
|
mAppState = LauncherAppState.getInstance(mTargetContext);
|
||||||
|
|
||||||
idp.numColumns = 5;
|
mExistingScreens = new IntArray();
|
||||||
idp.numRows = 5;
|
mScreenOccupancy = new IntSparseArrayMap<>();
|
||||||
|
mNewScreens = new IntArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AddWorkspaceItemsTask newTask(ItemInfo... items) {
|
private AddWorkspaceItemsTask newTask(ItemInfo... items) {
|
||||||
|
@ -70,17 +86,17 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
// Second screen has 2 holes of sizes 3x2 and 2x3
|
// 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));
|
setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
|
||||||
|
|
||||||
int[] spaceFound = newTask()
|
int[] spaceFound = newTask().findSpaceForItem(
|
||||||
.findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 1, 1);
|
mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 1, 1);
|
||||||
assertEquals(2, spaceFound[0]);
|
assertEquals(2, spaceFound[0]);
|
||||||
assertTrue(screenOccupancy.get(spaceFound[0])
|
assertTrue(mScreenOccupancy.get(spaceFound[0])
|
||||||
.isRegionVacant(spaceFound[1], spaceFound[2], 1, 1));
|
.isRegionVacant(spaceFound[1], spaceFound[2], 1, 1));
|
||||||
|
|
||||||
// Find a larger space
|
// Find a larger space
|
||||||
spaceFound = newTask()
|
spaceFound = newTask().findSpaceForItem(
|
||||||
.findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 2, 3);
|
mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 2, 3);
|
||||||
assertEquals(2, spaceFound[0]);
|
assertEquals(2, spaceFound[0]);
|
||||||
assertTrue(screenOccupancy.get(spaceFound[0])
|
assertTrue(mScreenOccupancy.get(spaceFound[0])
|
||||||
.isRegionVacant(spaceFound[1], spaceFound[2], 2, 3));
|
.isRegionVacant(spaceFound[1], spaceFound[2], 2, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,11 +105,11 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
// First screen has 2 holes of sizes 3x2 and 2x3
|
// 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));
|
setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
|
||||||
|
|
||||||
IntArray oldScreens = existingScreens.clone();
|
IntArray oldScreens = mExistingScreens.clone();
|
||||||
int[] spaceFound = newTask()
|
int[] spaceFound = newTask().findSpaceForItem(
|
||||||
.findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 3, 3);
|
mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 3, 3);
|
||||||
assertFalse(oldScreens.contains(spaceFound[0]));
|
assertFalse(oldScreens.contains(spaceFound[0]));
|
||||||
assertTrue(newScreens.contains(spaceFound[0]));
|
assertTrue(mNewScreens.contains(spaceFound[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -105,11 +121,14 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
||||||
|
|
||||||
// Nothing was added
|
// Nothing was added
|
||||||
assertTrue(executeTaskForTest(newTask(info)).isEmpty());
|
assertTrue(mModelHelper.executeTaskForTest(newTask(info)).isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddItem_some_items_added() throws Exception {
|
public void testAddItem_some_items_added() throws Exception {
|
||||||
|
Callbacks callbacks = mock(Callbacks.class);
|
||||||
|
mModelHelper.getModel().initialize(callbacks);
|
||||||
|
|
||||||
WorkspaceItemInfo info = new WorkspaceItemInfo();
|
WorkspaceItemInfo info = new WorkspaceItemInfo();
|
||||||
info.intent = new Intent().setComponent(mComponent1);
|
info.intent = new Intent().setComponent(mComponent1);
|
||||||
|
|
||||||
|
@ -119,7 +138,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
// Setup a screen with a hole
|
// Setup a screen with a hole
|
||||||
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
||||||
|
|
||||||
executeTaskForTest(newTask(info, info2)).get(0).run();
|
mModelHelper.executeTaskForTest(newTask(info, info2)).get(0).run();
|
||||||
ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class);
|
ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class);
|
||||||
ArgumentCaptor<ArrayList> animated = ArgumentCaptor.forClass(ArrayList.class);
|
ArgumentCaptor<ArrayList> animated = ArgumentCaptor.forClass(ArrayList.class);
|
||||||
|
|
||||||
|
@ -134,18 +153,23 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int setupWorkspaceWithHoles(int startId, int screenId, Rect... holes) throws Exception {
|
private int setupWorkspaceWithHoles(int startId, int screenId, Rect... holes) throws Exception {
|
||||||
GridOccupancy occupancy = new GridOccupancy(idp.numColumns, idp.numRows);
|
return mModelHelper.executeSimpleTask(
|
||||||
occupancy.markCells(0, 0, idp.numColumns, idp.numRows, true);
|
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) {
|
for (Rect r : holes) {
|
||||||
occupancy.markCells(r, false);
|
occupancy.markCells(r, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
existingScreens.add(screenId);
|
mExistingScreens.add(screenId);
|
||||||
screenOccupancy.append(screenId, occupancy);
|
mScreenOccupancy.append(screenId, occupancy);
|
||||||
|
|
||||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
for (int x = 0; x < mIdp.numColumns; x++) {
|
||||||
for (int x = 0; x < idp.numColumns; x++) {
|
for (int y = 0; y < mIdp.numRows; y++) {
|
||||||
for (int y = 0; y < idp.numRows; y++) {
|
|
||||||
if (!occupancy.cells[x][y]) {
|
if (!occupancy.cells[x][y]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -157,20 +181,15 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
info.cellX = x;
|
info.cellX = x;
|
||||||
info.cellY = y;
|
info.cellY = y;
|
||||||
info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||||
bgDataModel.addItem(targetContext, info, false);
|
bgDataModel.addItem(mTargetContext, info, false);
|
||||||
|
|
||||||
executor.execute(() -> {
|
ContentWriter writer = new ContentWriter(mTargetContext);
|
||||||
ContentWriter writer = new ContentWriter(targetContext);
|
info.writeToValues(writer);
|
||||||
info.writeToValues(writer);
|
writer.put(Favorites._ID, info.id);
|
||||||
writer.put(Favorites._ID, info.id);
|
mTargetContext.getContentResolver().insert(Favorites.CONTENT_URI,
|
||||||
targetContext.getContentResolver().insert(Favorites.CONTENT_URI,
|
writer.getValues(mTargetContext));
|
||||||
writer.getValues(targetContext));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.submit(() -> null).get();
|
|
||||||
executor.shutdown();
|
|
||||||
return startId;
|
return startId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
package com.android.launcher3.model;
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
|
|
||||||
import com.android.launcher3.LauncherProvider;
|
|
||||||
import com.android.launcher3.LauncherSettings;
|
|
||||||
import com.android.launcher3.util.TestLauncherProvider;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.robolectric.Robolectric;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.shadows.ShadowContentResolver;
|
|
||||||
import org.robolectric.shadows.ShadowLog;
|
|
||||||
|
|
||||||
public abstract class BaseGridChangesTestCase {
|
|
||||||
|
|
||||||
|
|
||||||
public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
|
||||||
public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
|
||||||
|
|
||||||
public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
|
||||||
public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
|
||||||
public static final int NO__ICON = -1;
|
|
||||||
|
|
||||||
public static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
|
|
||||||
|
|
||||||
public Context mContext;
|
|
||||||
public TestLauncherProvider mProvider;
|
|
||||||
public SQLiteDatabase mDb;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUpBaseCase() {
|
|
||||||
ShadowLog.stream = System.out;
|
|
||||||
|
|
||||||
mContext = RuntimeEnvironment.application;
|
|
||||||
mProvider = Robolectric.setupContentProvider(TestLauncherProvider.class);
|
|
||||||
ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, mProvider);
|
|
||||||
mDb = mProvider.getDb();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a dummy item in the DB.
|
|
||||||
* @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
|
|
||||||
* folder (where the type represents the number of items in the folder).
|
|
||||||
*/
|
|
||||||
public int addItem(int type, int screen, int container, int x, int y) {
|
|
||||||
int id = LauncherSettings.Settings.call(mContext.getContentResolver(),
|
|
||||||
LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
|
|
||||||
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
|
||||||
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(LauncherSettings.Favorites._ID, id);
|
|
||||||
values.put(LauncherSettings.Favorites.CONTAINER, container);
|
|
||||||
values.put(LauncherSettings.Favorites.SCREEN, screen);
|
|
||||||
values.put(LauncherSettings.Favorites.CELLX, x);
|
|
||||||
values.put(LauncherSettings.Favorites.CELLY, y);
|
|
||||||
values.put(LauncherSettings.Favorites.SPANX, 1);
|
|
||||||
values.put(LauncherSettings.Favorites.SPANY, 1);
|
|
||||||
|
|
||||||
if (type == APP_ICON || type == SHORTCUT) {
|
|
||||||
values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
|
|
||||||
values.put(LauncherSettings.Favorites.INTENT,
|
|
||||||
new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0));
|
|
||||||
} else {
|
|
||||||
values.put(LauncherSettings.Favorites.ITEM_TYPE,
|
|
||||||
LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
|
|
||||||
// Add folder items.
|
|
||||||
for (int i = 0; i < type; i++) {
|
|
||||||
addItem(APP_ICON, 0, id, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mContext.getContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[][][] createGrid(int[][][] typeArray) {
|
|
||||||
return createGrid(typeArray, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the DB with dummy elements to represent the provided grid structure.
|
|
||||||
* @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
|
|
||||||
* type definitions. The first dimension represents the screens and the next
|
|
||||||
* two represent the workspace grid.
|
|
||||||
* @param startScreen First screen id from where the icons will be added.
|
|
||||||
* @return the same grid representation where each entry is the corresponding item id.
|
|
||||||
*/
|
|
||||||
public int[][][] createGrid(int[][][] typeArray, int startScreen) {
|
|
||||||
LauncherSettings.Settings.call(mContext.getContentResolver(),
|
|
||||||
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
|
||||||
int[][][] ids = new int[typeArray.length][][];
|
|
||||||
|
|
||||||
for (int i = 0; i < typeArray.length; i++) {
|
|
||||||
// Add screen to DB
|
|
||||||
int screenId = startScreen + i;
|
|
||||||
|
|
||||||
// Keep the screen id counter up to date
|
|
||||||
LauncherSettings.Settings.call(mContext.getContentResolver(),
|
|
||||||
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
|
||||||
|
|
||||||
ids[i] = new int[typeArray[i].length][];
|
|
||||||
for (int y = 0; y < typeArray[i].length; y++) {
|
|
||||||
ids[i][y] = new int[typeArray[i][y].length];
|
|
||||||
for (int x = 0; x < typeArray[i][y].length; x++) {
|
|
||||||
if (typeArray[i][y][x] < 0) {
|
|
||||||
// Empty cell
|
|
||||||
ids[i][y][x] = -1;
|
|
||||||
} else {
|
|
||||||
ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,231 +0,0 @@
|
||||||
package com.android.launcher3.model;
|
|
||||||
|
|
||||||
import static com.android.launcher3.shadows.ShadowLooperExecutor.reinitializeStaticExecutors;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Bitmap.Config;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Process;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.android.launcher3.AppFilter;
|
|
||||||
import com.android.launcher3.AppInfo;
|
|
||||||
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.ModelUpdateTask;
|
|
||||||
import com.android.launcher3.LauncherProvider;
|
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
|
||||||
import com.android.launcher3.icons.IconCache;
|
|
||||||
import com.android.launcher3.icons.cache.CachingLogic;
|
|
||||||
import com.android.launcher3.model.BgDataModel.Callbacks;
|
|
||||||
import com.android.launcher3.pm.InstallSessionHelper;
|
|
||||||
import com.android.launcher3.util.ComponentKey;
|
|
||||||
import com.android.launcher3.util.TestLauncherProvider;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.robolectric.Robolectric;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.shadows.ShadowContentResolver;
|
|
||||||
import org.robolectric.shadows.ShadowLog;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for writing tests for Model update tasks.
|
|
||||||
*/
|
|
||||||
public class BaseModelUpdateTaskTestCase {
|
|
||||||
|
|
||||||
public final HashMap<Class, HashMap<String, Field>> fieldCache = new HashMap<>();
|
|
||||||
public TestLauncherProvider provider;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
ShadowLog.stream = System.out;
|
|
||||||
reinitializeStaticExecutors();
|
|
||||||
InstallSessionHelper.INSTANCE.initializeForTesting(null);
|
|
||||||
|
|
||||||
provider = Robolectric.setupContentProvider(TestLauncherProvider.class);
|
|
||||||
ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, provider);
|
|
||||||
|
|
||||||
callbacks = mock(Callbacks.class);
|
|
||||||
appState = mock(LauncherAppState.class);
|
|
||||||
model = mock(LauncherModel.class);
|
|
||||||
modelWriter = mock(ModelWriter.class);
|
|
||||||
|
|
||||||
LauncherAppState.INSTANCE.initializeForTesting(appState);
|
|
||||||
when(appState.getModel()).thenReturn(model);
|
|
||||||
when(model.getWriter(anyBoolean(), anyBoolean())).thenReturn(modelWriter);
|
|
||||||
when(model.getCallback()).thenReturn(callbacks);
|
|
||||||
|
|
||||||
myUser = Process.myUserHandle();
|
|
||||||
|
|
||||||
bgDataModel = new BgDataModel();
|
|
||||||
targetContext = RuntimeEnvironment.application;
|
|
||||||
|
|
||||||
idp = new InvariantDeviceProfile();
|
|
||||||
iconCache = new MyIconCache(targetContext, idp);
|
|
||||||
|
|
||||||
allAppsList = new AllAppsList(iconCache, new AppFilter());
|
|
||||||
|
|
||||||
when(appState.getIconCache()).thenReturn(iconCache);
|
|
||||||
when(appState.getInvariantDeviceProfile()).thenReturn(idp);
|
|
||||||
when(appState.getContext()).thenReturn(targetContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronously executes the task and returns all the UI callbacks posted.
|
|
||||||
*/
|
|
||||||
public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
|
|
||||||
when(model.isModelLoaded()).thenReturn(true);
|
|
||||||
|
|
||||||
Executor mockExecutor = mock(Executor.class);
|
|
||||||
|
|
||||||
task.init(appState, model, bgDataModel, allAppsList, mockExecutor);
|
|
||||||
task.run();
|
|
||||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
|
||||||
verify(mockExecutor, atLeast(0)).execute(captor.capture());
|
|
||||||
|
|
||||||
return captor.getAllValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes mock data for the test.
|
|
||||||
*/
|
|
||||||
public void initializeData(String resourceName) throws Exception {
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
|
|
||||||
this.getClass().getResourceAsStream(resourceName)))) {
|
|
||||||
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 <T> CacheEntry cacheLocked(
|
|
||||||
@NonNull ComponentName componentName,
|
|
||||||
UserHandle user, @NonNull Supplier<T> infoProvider,
|
|
||||||
@NonNull CachingLogic<T> cachingLogic,
|
|
||||||
boolean usePackageIcon, boolean useLowResIcon) {
|
|
||||||
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
|
|
||||||
if (entry == null) {
|
|
||||||
entry = new CacheEntry();
|
|
||||||
entry.bitmap = getDefaultIcon(user);
|
|
||||||
}
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addCache(ComponentName key, String title) {
|
|
||||||
CacheEntry entry = new CacheEntry();
|
|
||||||
entry.bitmap = BitmapInfo.of(newIcon(), Color.RED);
|
|
||||||
entry.title = title;
|
|
||||||
mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap newIcon() {
|
|
||||||
return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
|
|
||||||
return BitmapInfo.fromBitmap(newIcon());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,15 +5,34 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotSame;
|
import static org.junit.Assert.assertNotSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Bitmap.Config;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.android.launcher3.AppInfo;
|
import com.android.launcher3.AppInfo;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfo;
|
||||||
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.WorkspaceItemInfo;
|
import com.android.launcher3.WorkspaceItemInfo;
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
|
import com.android.launcher3.icons.IconCache;
|
||||||
|
import com.android.launcher3.icons.cache.CachingLogic;
|
||||||
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.LooperMode;
|
||||||
|
import org.robolectric.annotation.LooperMode.Mode;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -21,40 +40,73 @@ import java.util.HashSet;
|
||||||
/**
|
/**
|
||||||
* Tests for {@link CacheDataUpdatedTask}
|
* Tests for {@link CacheDataUpdatedTask}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
|
@LooperMode(Mode.PAUSED)
|
||||||
|
public class CacheDataUpdatedTaskTest {
|
||||||
|
|
||||||
private static final String NEW_LABEL_PREFIX = "new-label-";
|
private static final String NEW_LABEL_PREFIX = "new-label-";
|
||||||
|
|
||||||
|
private LauncherModelHelper mModelHelper;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void initData() throws Exception {
|
public void setup() throws Exception {
|
||||||
initializeData("/cache_data_updated_task_data.txt");
|
mModelHelper = new LauncherModelHelper();
|
||||||
|
mModelHelper.initializeData("/cache_data_updated_task_data.txt");
|
||||||
|
|
||||||
// Add dummy entries in the cache to simulate update
|
// Add dummy entries in the cache to simulate update
|
||||||
for (ItemInfo info : bgDataModel.itemsIdMap) {
|
Context context = RuntimeEnvironment.application;
|
||||||
iconCache.addCache(info.getTargetComponent(), NEW_LABEL_PREFIX + info.id);
|
IconCache iconCache = LauncherAppState.getInstance(context).getIconCache();
|
||||||
|
CachingLogic<ItemInfo> dummyLogic = new CachingLogic<ItemInfo>() {
|
||||||
|
@Override
|
||||||
|
public ComponentName getComponent(ItemInfo info) {
|
||||||
|
return info.getTargetComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserHandle getUser(ItemInfo info) {
|
||||||
|
return info.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getLabel(ItemInfo info) {
|
||||||
|
return NEW_LABEL_PREFIX + info.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public BitmapInfo loadIcon(Context context, ItemInfo info) {
|
||||||
|
return BitmapInfo.of(Bitmap.createBitmap(1, 1, Config.ARGB_8888), Color.RED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
UserManager um = context.getSystemService(UserManager.class);
|
||||||
|
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
|
||||||
|
iconCache.addIconToDBAndMemCache(info, dummyLogic, new PackageInfo(),
|
||||||
|
um.getSerialNumberForUser(info.user), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CacheDataUpdatedTask newTask(int op, String... pkg) {
|
private CacheDataUpdatedTask newTask(int op, String... pkg) {
|
||||||
return new CacheDataUpdatedTask(op, myUser, new HashSet<>(Arrays.asList(pkg)));
|
return new CacheDataUpdatedTask(op, Process.myUserHandle(),
|
||||||
|
new HashSet<>(Arrays.asList(pkg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCacheUpdate_update_apps() throws Exception {
|
public void testCacheUpdate_update_apps() throws Exception {
|
||||||
// Clear all icons from apps list so that its easy to check what was updated
|
// Clear all icons from apps list so that its easy to check what was updated
|
||||||
for (AppInfo info : allAppsList.data) {
|
for (AppInfo info : mModelHelper.getAllAppsList().data) {
|
||||||
info.bitmap = BitmapInfo.LOW_RES_INFO;
|
info.bitmap = BitmapInfo.LOW_RES_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
|
mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
|
||||||
|
|
||||||
// Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
|
// Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
|
||||||
// is not updated
|
// is not updated
|
||||||
verifyUpdate(1, 2);
|
verifyUpdate(1, 2);
|
||||||
|
|
||||||
// Verify that only app1 var updated in allAppsList
|
// Verify that only app1 var updated in allAppsList
|
||||||
assertFalse(allAppsList.data.isEmpty());
|
assertFalse(mModelHelper.getAllAppsList().data.isEmpty());
|
||||||
for (AppInfo info : allAppsList.data) {
|
for (AppInfo info : mModelHelper.getAllAppsList().data) {
|
||||||
if (info.componentName.getPackageName().equals("app1")) {
|
if (info.componentName.getPackageName().equals("app1")) {
|
||||||
assertFalse(info.bitmap.isNullOrLowRes());
|
assertFalse(info.bitmap.isNullOrLowRes());
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,7 +117,7 @@ public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionUpdate_ignores_normal_apps() throws Exception {
|
public void testSessionUpdate_ignores_normal_apps() throws Exception {
|
||||||
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
|
mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
|
||||||
|
|
||||||
// app1 has no restored shortcuts. Verify that nothing was updated.
|
// app1 has no restored shortcuts. Verify that nothing was updated.
|
||||||
verifyUpdate();
|
verifyUpdate();
|
||||||
|
@ -73,7 +125,7 @@ public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionUpdate_updates_pending_apps() throws Exception {
|
public void testSessionUpdate_updates_pending_apps() throws Exception {
|
||||||
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
|
mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
|
||||||
|
|
||||||
// app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were
|
// app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were
|
||||||
// were updated
|
// were updated
|
||||||
|
@ -82,7 +134,7 @@ public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
|
|
||||||
private void verifyUpdate(Integer... idsUpdated) {
|
private void verifyUpdate(Integer... idsUpdated) {
|
||||||
HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
|
HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
|
||||||
for (ItemInfo info : bgDataModel.itemsIdMap) {
|
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
|
||||||
if (updates.contains(info.id)) {
|
if (updates.contains(info.id)) {
|
||||||
assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
|
assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
|
||||||
assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
|
assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
|
||||||
|
|
|
@ -36,11 +36,11 @@ import com.android.launcher3.LauncherProvider;
|
||||||
import com.android.launcher3.LauncherProvider.DatabaseHelper;
|
import com.android.launcher3.LauncherProvider.DatabaseHelper;
|
||||||
import com.android.launcher3.LauncherSettings.Favorites;
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -48,7 +48,7 @@ import java.io.File;
|
||||||
/**
|
/**
|
||||||
* Tests for {@link DbDowngradeHelper}
|
* Tests for {@link DbDowngradeHelper}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class DbDowngradeHelperTest {
|
public class DbDowngradeHelperTest {
|
||||||
|
|
||||||
private static final String SCHEMA_FILE = "test_schema.json";
|
private static final String SCHEMA_FILE = "test_schema.json";
|
||||||
|
|
|
@ -22,6 +22,7 @@ import static org.robolectric.Shadows.shadowOf;
|
||||||
import static org.robolectric.util.ReflectionHelpers.setField;
|
import static org.robolectric.util.ReflectionHelpers.setField;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.pm.PackageInstaller;
|
import android.content.pm.PackageInstaller;
|
||||||
import android.content.pm.PackageInstaller.SessionInfo;
|
import android.content.pm.PackageInstaller.SessionInfo;
|
||||||
import android.content.pm.PackageInstaller.SessionParams;
|
import android.content.pm.PackageInstaller.SessionParams;
|
||||||
|
@ -31,23 +32,20 @@ import android.provider.Settings;
|
||||||
import com.android.launcher3.FolderInfo;
|
import com.android.launcher3.FolderInfo;
|
||||||
import com.android.launcher3.InvariantDeviceProfile;
|
import com.android.launcher3.InvariantDeviceProfile;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfo;
|
||||||
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.LauncherProvider;
|
import com.android.launcher3.LauncherProvider;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
import com.android.launcher3.pm.InstallSessionHelper;
|
import com.android.launcher3.model.BgDataModel.Callbacks;
|
||||||
import com.android.launcher3.shadows.LShadowLauncherApps;
|
|
||||||
import com.android.launcher3.shadows.LShadowUserManager;
|
|
||||||
import com.android.launcher3.shadows.ShadowLooperExecutor;
|
|
||||||
import com.android.launcher3.util.Executors;
|
import com.android.launcher3.util.Executors;
|
||||||
import com.android.launcher3.util.LauncherLayoutBuilder;
|
import com.android.launcher3.util.LauncherLayoutBuilder;
|
||||||
import com.android.launcher3.widget.custom.CustomWidgetManager;
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
import org.robolectric.annotation.LooperMode;
|
import org.robolectric.annotation.LooperMode;
|
||||||
import org.robolectric.annotation.LooperMode.Mode;
|
import org.robolectric.annotation.LooperMode.Mode;
|
||||||
import org.robolectric.shadows.ShadowPackageManager;
|
import org.robolectric.shadows.ShadowPackageManager;
|
||||||
|
@ -61,10 +59,9 @@ import java.util.ArrayList;
|
||||||
/**
|
/**
|
||||||
* Tests for layout parser for remote layout
|
* Tests for layout parser for remote layout
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
@Config(shadows = {LShadowUserManager.class, LShadowLauncherApps.class, ShadowLooperExecutor.class})
|
|
||||||
@LooperMode(Mode.PAUSED)
|
@LooperMode(Mode.PAUSED)
|
||||||
public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
|
public class DefaultLayoutProviderTest {
|
||||||
|
|
||||||
private static final String SETTINGS_APP = "com.android.settings";
|
private static final String SETTINGS_APP = "com.android.settings";
|
||||||
private static final String TEST_PROVIDER_AUTHORITY =
|
private static final String TEST_PROVIDER_AUTHORITY =
|
||||||
|
@ -73,40 +70,37 @@ public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
|
||||||
private static final int BITMAP_SIZE = 10;
|
private static final int BITMAP_SIZE = 10;
|
||||||
private static final int GRID_SIZE = 4;
|
private static final int GRID_SIZE = 4;
|
||||||
|
|
||||||
|
private LauncherModelHelper mModelHelper;
|
||||||
|
private Context mTargetContext;
|
||||||
|
private InvariantDeviceProfile mIdp;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() {
|
||||||
super.setUp();
|
mModelHelper = new LauncherModelHelper();
|
||||||
InvariantDeviceProfile.INSTANCE.initializeForTesting(idp);
|
mTargetContext = RuntimeEnvironment.application;
|
||||||
CustomWidgetManager.INSTANCE.initializeForTesting(mock(CustomWidgetManager.class));
|
|
||||||
|
|
||||||
idp.numRows = idp.numColumns = idp.numHotseatIcons = GRID_SIZE;
|
mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
|
||||||
idp.iconBitmapSize = BITMAP_SIZE;
|
mIdp.numRows = mIdp.numColumns = mIdp.numHotseatIcons = GRID_SIZE;
|
||||||
|
mIdp.iconBitmapSize = BITMAP_SIZE;
|
||||||
|
|
||||||
provider.setAllowLoadDefaultFavorites(true);
|
mModelHelper.provider.setAllowLoadDefaultFavorites(true);
|
||||||
Settings.Secure.putString(targetContext.getContentResolver(),
|
Settings.Secure.putString(mTargetContext.getContentResolver(),
|
||||||
"launcher3.layout.provider", TEST_PROVIDER_AUTHORITY);
|
"launcher3.layout.provider", TEST_PROVIDER_AUTHORITY);
|
||||||
|
|
||||||
ShadowPackageManager spm = shadowOf(targetContext.getPackageManager());
|
ShadowPackageManager spm = shadowOf(mTargetContext.getPackageManager());
|
||||||
spm.addProviderIfNotPresent(new ComponentName("com.test", "Dummy")).authority =
|
spm.addProviderIfNotPresent(new ComponentName("com.test", "Dummy")).authority =
|
||||||
TEST_PROVIDER_AUTHORITY;
|
TEST_PROVIDER_AUTHORITY;
|
||||||
spm.addActivityIfNotPresent(new ComponentName(SETTINGS_APP, SETTINGS_APP));
|
spm.addActivityIfNotPresent(new ComponentName(SETTINGS_APP, SETTINGS_APP));
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
|
||||||
public void cleanup() {
|
|
||||||
InvariantDeviceProfile.INSTANCE.initializeForTesting(null);
|
|
||||||
CustomWidgetManager.INSTANCE.initializeForTesting(null);
|
|
||||||
InstallSessionHelper.INSTANCE.initializeForTesting(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
|
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
|
||||||
writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0)
|
writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0)
|
||||||
.putApp(SETTINGS_APP, SETTINGS_APP));
|
.putApp(SETTINGS_APP, SETTINGS_APP));
|
||||||
|
|
||||||
// Verify one item in hotseat
|
// Verify one item in hotseat
|
||||||
assertEquals(1, bgDataModel.workspaceItems.size());
|
assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
|
||||||
ItemInfo info = bgDataModel.workspaceItems.get(0);
|
ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
|
||||||
assertEquals(LauncherSettings.Favorites.CONTAINER_HOTSEAT, info.container);
|
assertEquals(LauncherSettings.Favorites.CONTAINER_HOTSEAT, info.container);
|
||||||
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPLICATION, info.itemType);
|
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPLICATION, info.itemType);
|
||||||
}
|
}
|
||||||
|
@ -120,8 +114,8 @@ public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
// Verify folder
|
// Verify folder
|
||||||
assertEquals(1, bgDataModel.workspaceItems.size());
|
assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
|
||||||
ItemInfo info = bgDataModel.workspaceItems.get(0);
|
ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
|
||||||
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
|
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
|
||||||
assertEquals(3, ((FolderInfo) info).contents.size());
|
assertEquals(3, ((FolderInfo) info).contents.size());
|
||||||
}
|
}
|
||||||
|
@ -134,7 +128,7 @@ public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
|
||||||
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
||||||
params.setAppPackageName(pendingAppPkg);
|
params.setAppPackageName(pendingAppPkg);
|
||||||
|
|
||||||
PackageInstaller installer = targetContext.getPackageManager().getPackageInstaller();
|
PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
|
||||||
int sessionId = installer.createSession(params);
|
int sessionId = installer.createSession(params);
|
||||||
SessionInfo sessionInfo = installer.getSessionInfo(sessionId);
|
SessionInfo sessionInfo = installer.getSessionInfo(sessionId);
|
||||||
setField(sessionInfo, "installerPackageName", "com.test");
|
setField(sessionInfo, "installerPackageName", "com.test");
|
||||||
|
@ -144,8 +138,8 @@ public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
|
||||||
.putWidget(pendingAppPkg, "DummyWidget", 2, 2));
|
.putWidget(pendingAppPkg, "DummyWidget", 2, 2));
|
||||||
|
|
||||||
// Verify widget
|
// Verify widget
|
||||||
assertEquals(1, bgDataModel.appWidgets.size());
|
assertEquals(1, mModelHelper.getBgDataModel().appWidgets.size());
|
||||||
ItemInfo info = bgDataModel.appWidgets.get(0);
|
ItemInfo info = mModelHelper.getBgDataModel().appWidgets.get(0);
|
||||||
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET, info.itemType);
|
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET, info.itemType);
|
||||||
assertEquals(2, info.spanX);
|
assertEquals(2, info.spanX);
|
||||||
assertEquals(2, info.spanY);
|
assertEquals(2, info.spanY);
|
||||||
|
@ -155,13 +149,21 @@ public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
builder.build(new OutputStreamWriter(bos));
|
builder.build(new OutputStreamWriter(bos));
|
||||||
|
|
||||||
Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, targetContext);
|
Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, mTargetContext);
|
||||||
shadowOf(targetContext.getContentResolver()).registerInputStream(layoutUri,
|
shadowOf(mTargetContext.getContentResolver()).registerInputStream(layoutUri,
|
||||||
new ByteArrayInputStream(bos.toByteArray()));
|
new ByteArrayInputStream(bos.toByteArray()));
|
||||||
|
|
||||||
LoaderResults results = new LoaderResults(appState, bgDataModel, allAppsList, 0,
|
LoaderResults results = new LoaderResults(
|
||||||
new WeakReference<>(callbacks));
|
LauncherAppState.getInstance(mTargetContext),
|
||||||
LoaderTask task = new LoaderTask(appState, allAppsList, bgDataModel, results);
|
mModelHelper.getBgDataModel(),
|
||||||
|
mModelHelper.getAllAppsList(),
|
||||||
|
0,
|
||||||
|
new WeakReference<>(mock(Callbacks.class)));
|
||||||
|
LoaderTask task = new LoaderTask(
|
||||||
|
LauncherAppState.getInstance(mTargetContext),
|
||||||
|
mModelHelper.getAllAppsList(),
|
||||||
|
mModelHelper.getBgDataModel(),
|
||||||
|
results);
|
||||||
Executors.MODEL_EXECUTOR.submit(() -> task.loadWorkspace(new ArrayList<>())).get();
|
Executors.MODEL_EXECUTOR.submit(() -> task.loadWorkspace(new ArrayList<>())).get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,33 +6,53 @@ import static android.database.DatabaseUtils.queryNumEntries;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
|
import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
|
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
|
||||||
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
|
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.DESKTOP;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.NO__ICON;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
|
||||||
import com.android.launcher3.LauncherSettings.Favorites;
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
import com.android.launcher3.LauncherSettings.Settings;
|
import com.android.launcher3.LauncherSettings.Settings;
|
||||||
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link GridBackupTable}
|
* Unit tests for {@link GridBackupTable}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class GridBackupTableTest extends BaseGridChangesTestCase {
|
public class GridBackupTableTest {
|
||||||
|
|
||||||
private static final int BACKUP_ITEM_COUNT = 12;
|
private static final int BACKUP_ITEM_COUNT = 12;
|
||||||
|
|
||||||
|
private LauncherModelHelper mModelHelper;
|
||||||
|
private Context mContext;
|
||||||
|
private SQLiteDatabase mDb;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setupGridData() {
|
public void setUp() {
|
||||||
createGrid(new int[][][]{{
|
mModelHelper = new LauncherModelHelper();
|
||||||
|
mContext = RuntimeEnvironment.application;
|
||||||
|
mDb = mModelHelper.provider.getDb();
|
||||||
|
|
||||||
|
setupGridData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupGridData() {
|
||||||
|
mModelHelper.createGrid(new int[][][]{{
|
||||||
{ APP_ICON, APP_ICON, SHORTCUT, SHORTCUT},
|
{ APP_ICON, APP_ICON, SHORTCUT, SHORTCUT},
|
||||||
{ SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
|
{ SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
|
||||||
{ NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
|
{ NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
|
||||||
|
@ -81,7 +101,7 @@ public class GridBackupTableTest extends BaseGridChangesTestCase {
|
||||||
|
|
||||||
assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
|
assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
|
||||||
|
|
||||||
addItem(1, 2, DESKTOP, 1, 1);
|
mModelHelper.addItem(1, 2, DESKTOP, 1, 1);
|
||||||
assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
|
assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,31 @@
|
||||||
package com.android.launcher3.model;
|
package com.android.launcher3.model;
|
||||||
|
|
||||||
import static com.android.launcher3.model.GridSizeMigrationTask.getWorkspaceScreenIds;
|
import static com.android.launcher3.model.GridSizeMigrationTask.getWorkspaceScreenIds;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.HOTSEAT;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
|
||||||
import com.android.launcher3.InvariantDeviceProfile;
|
import com.android.launcher3.InvariantDeviceProfile;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.config.FlagOverrideRule;
|
|
||||||
import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
|
import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
|
||||||
import com.android.launcher3.util.IntArray;
|
import com.android.launcher3.util.IntArray;
|
||||||
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -27,30 +33,35 @@ import java.util.LinkedList;
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link GridSizeMigrationTask}
|
* Unit tests for {@link GridSizeMigrationTask}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
|
public class GridSizeMigrationTaskTest {
|
||||||
|
|
||||||
@Rule
|
private LauncherModelHelper mModelHelper;
|
||||||
public final FlagOverrideRule flags = new FlagOverrideRule();
|
private Context mContext;
|
||||||
|
private SQLiteDatabase mDb;
|
||||||
|
|
||||||
private HashSet<String> mValidPackages;
|
private HashSet<String> mValidPackages;
|
||||||
private InvariantDeviceProfile mIdp;
|
private InvariantDeviceProfile mIdp;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mModelHelper = new LauncherModelHelper();
|
||||||
|
mContext = RuntimeEnvironment.application;
|
||||||
|
mDb = mModelHelper.provider.getDb();
|
||||||
|
|
||||||
mValidPackages = new HashSet<>();
|
mValidPackages = new HashSet<>();
|
||||||
mValidPackages.add(TEST_PACKAGE);
|
mValidPackages.add(TEST_PACKAGE);
|
||||||
mIdp = new InvariantDeviceProfile();
|
mIdp = InvariantDeviceProfile.INSTANCE.get(mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHotseatMigration_apps_dropped() throws Exception {
|
public void testHotseatMigration_apps_dropped() throws Exception {
|
||||||
int[] hotseatItems = {
|
int[] hotseatItems = {
|
||||||
addItem(APP_ICON, 0, HOTSEAT, 0, 0),
|
mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0),
|
||||||
addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
|
mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
|
||||||
-1,
|
-1,
|
||||||
addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
|
mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
|
||||||
addItem(APP_ICON, 4, HOTSEAT, 0, 0),
|
mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
mIdp.numHotseatIcons = 3;
|
mIdp.numHotseatIcons = 3;
|
||||||
|
@ -63,11 +74,11 @@ public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
|
||||||
@Test
|
@Test
|
||||||
public void testHotseatMigration_shortcuts_dropped() throws Exception {
|
public void testHotseatMigration_shortcuts_dropped() throws Exception {
|
||||||
int[] hotseatItems = {
|
int[] hotseatItems = {
|
||||||
addItem(APP_ICON, 0, HOTSEAT, 0, 0),
|
mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0),
|
||||||
addItem(30, 1, HOTSEAT, 0, 0),
|
mModelHelper.addItem(30, 1, HOTSEAT, 0, 0),
|
||||||
-1,
|
-1,
|
||||||
addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
|
mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
|
||||||
addItem(10, 4, HOTSEAT, 0, 0),
|
mModelHelper.addItem(10, 4, HOTSEAT, 0, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
mIdp.numHotseatIcons = 3;
|
mIdp.numHotseatIcons = 3;
|
||||||
|
@ -109,7 +120,7 @@ public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWorkspace_empty_row_column_removed() throws Exception {
|
public void testWorkspace_empty_row_column_removed() throws Exception {
|
||||||
int[][][] ids = createGrid(new int[][][]{{
|
int[][][] ids = mModelHelper.createGrid(new int[][][]{{
|
||||||
{ 0, 0, -1, 1},
|
{ 0, 0, -1, 1},
|
||||||
{ 3, 1, -1, 4},
|
{ 3, 1, -1, 4},
|
||||||
{ -1, -1, -1, -1},
|
{ -1, -1, -1, -1},
|
||||||
|
@ -129,7 +140,7 @@ public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWorkspace_new_screen_created() throws Exception {
|
public void testWorkspace_new_screen_created() throws Exception {
|
||||||
int[][][] ids = createGrid(new int[][][]{{
|
int[][][] ids = mModelHelper.createGrid(new int[][][]{{
|
||||||
{ 0, 0, 0, 1},
|
{ 0, 0, 0, 1},
|
||||||
{ 3, 1, 0, 4},
|
{ 3, 1, 0, 4},
|
||||||
{ -1, -1, -1, -1},
|
{ -1, -1, -1, -1},
|
||||||
|
@ -151,7 +162,7 @@ public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWorkspace_items_merged_in_next_screen() throws Exception {
|
public void testWorkspace_items_merged_in_next_screen() throws Exception {
|
||||||
int[][][] ids = createGrid(new int[][][]{{
|
int[][][] ids = mModelHelper.createGrid(new int[][][]{{
|
||||||
{ 0, 0, 0, 1},
|
{ 0, 0, 0, 1},
|
||||||
{ 3, 1, 0, 4},
|
{ 3, 1, 0, 4},
|
||||||
{ -1, -1, -1, -1},
|
{ -1, -1, -1, -1},
|
||||||
|
@ -181,7 +192,7 @@ public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
|
||||||
public void testWorkspace_items_not_merged_in_next_screen() throws Exception {
|
public void testWorkspace_items_not_merged_in_next_screen() throws Exception {
|
||||||
// First screen has 2 items that need to be moved, but second screen has only one
|
// First screen has 2 items that need to be moved, but second screen has only one
|
||||||
// empty space after migration (top-left corner)
|
// empty space after migration (top-left corner)
|
||||||
int[][][] ids = createGrid(new int[][][]{{
|
int[][][] ids = mModelHelper.createGrid(new int[][][]{{
|
||||||
{ 0, 0, 0, 1},
|
{ 0, 0, 0, 1},
|
||||||
{ 3, 1, 0, 4},
|
{ 3, 1, 0, 4},
|
||||||
{ -1, -1, -1, -1},
|
{ -1, -1, -1, -1},
|
||||||
|
@ -217,7 +228,7 @@ public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
|
||||||
}
|
}
|
||||||
// The first screen has one item on the 4th column which needs moving, as the first row
|
// The first screen has one item on the 4th column which needs moving, as the first row
|
||||||
// will be kept empty.
|
// will be kept empty.
|
||||||
int[][][] ids = createGrid(new int[][][]{{
|
int[][][] ids = mModelHelper.createGrid(new int[][][]{{
|
||||||
{ -1, -1, -1, -1},
|
{ -1, -1, -1, -1},
|
||||||
{ 3, 1, 7, 0},
|
{ 3, 1, 7, 0},
|
||||||
{ 8, 7, 7, -1},
|
{ 8, 7, 7, -1},
|
||||||
|
@ -244,7 +255,7 @@ public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Items will get moved to the next screen to keep the first screen empty.
|
// Items will get moved to the next screen to keep the first screen empty.
|
||||||
int[][][] ids = createGrid(new int[][][]{{
|
int[][][] ids = mModelHelper.createGrid(new int[][][]{{
|
||||||
{ -1, -1, -1, -1},
|
{ -1, -1, -1, -1},
|
||||||
{ 0, 1, 0, 0},
|
{ 0, 1, 0, 0},
|
||||||
{ 8, 7, 7, -1},
|
{ 8, 7, 7, -1},
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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;
|
package com.android.launcher3.model;
|
||||||
|
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
|
import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
|
||||||
|
@ -17,6 +33,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.RESTORED;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.SCREEN;
|
import static com.android.launcher3.LauncherSettings.Favorites.SCREEN;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
|
import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites._ID;
|
import static com.android.launcher3.LauncherSettings.Favorites._ID;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static junit.framework.Assert.assertEquals;
|
||||||
import static junit.framework.Assert.assertFalse;
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
@ -24,43 +41,38 @@ import static junit.framework.Assert.assertNotNull;
|
||||||
import static junit.framework.Assert.assertNull;
|
import static junit.framework.Assert.assertNull;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.LauncherApps;
|
import android.content.pm.LauncherApps;
|
||||||
import android.database.MatrixCursor;
|
import android.database.MatrixCursor;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.filters.SmallTest;
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.launcher3.InvariantDeviceProfile;
|
import com.android.launcher3.InvariantDeviceProfile;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfo;
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.WorkspaceItemInfo;
|
import com.android.launcher3.WorkspaceItemInfo;
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
import com.android.launcher3.util.Executors;
|
||||||
import com.android.launcher3.icons.IconCache;
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
import com.android.launcher3.util.PackageManagerHelper;
|
import com.android.launcher3.util.PackageManagerHelper;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.LooperMode;
|
||||||
|
import org.robolectric.annotation.LooperMode.Mode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link LoaderCursor}
|
* Tests for {@link LoaderCursor}
|
||||||
*/
|
*/
|
||||||
@SmallTest
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
@RunWith(AndroidJUnit4.class)
|
@LooperMode(Mode.PAUSED)
|
||||||
public class LoaderCursorTest {
|
public class LoaderCursorTest {
|
||||||
|
|
||||||
private LauncherAppState mMockApp;
|
private LauncherAppState mApp;
|
||||||
private IconCache mMockIconCache;
|
|
||||||
|
|
||||||
private MatrixCursor mCursor;
|
private MatrixCursor mCursor;
|
||||||
private InvariantDeviceProfile mIDP;
|
private InvariantDeviceProfile mIDP;
|
||||||
|
@ -71,22 +83,18 @@ public class LoaderCursorTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
mIDP = new InvariantDeviceProfile();
|
mContext = RuntimeEnvironment.application;
|
||||||
|
mIDP = InvariantDeviceProfile.INSTANCE.get(mContext);
|
||||||
|
mApp = LauncherAppState.getInstance(mContext);
|
||||||
|
mLauncherApps = mContext.getSystemService(LauncherApps.class);
|
||||||
|
|
||||||
mCursor = new MatrixCursor(new String[] {
|
mCursor = new MatrixCursor(new String[] {
|
||||||
ICON, ICON_PACKAGE, ICON_RESOURCE, TITLE,
|
ICON, ICON_PACKAGE, ICON_RESOURCE, TITLE,
|
||||||
_ID, CONTAINER, ITEM_TYPE, PROFILE_ID,
|
_ID, CONTAINER, ITEM_TYPE, PROFILE_ID,
|
||||||
SCREEN, CELLX, CELLY, RESTORED, INTENT
|
SCREEN, CELLX, CELLY, RESTORED, INTENT
|
||||||
});
|
});
|
||||||
mContext = InstrumentationRegistry.getTargetContext();
|
|
||||||
|
|
||||||
mMockApp = mock(LauncherAppState.class);
|
mLoaderCursor = new LoaderCursor(mCursor, mApp);
|
||||||
mMockIconCache = mock(IconCache.class);
|
|
||||||
when(mMockApp.getIconCache()).thenReturn(mMockIconCache);
|
|
||||||
when(mMockApp.getInvariantDeviceProfile()).thenReturn(mIDP);
|
|
||||||
when(mMockApp.getContext()).thenReturn(mContext);
|
|
||||||
mLauncherApps = mContext.getSystemService(LauncherApps.class);
|
|
||||||
|
|
||||||
mLoaderCursor = new LoaderCursor(mCursor, mMockApp);
|
|
||||||
mLoaderCursor.allUsers.put(0, Process.myUserHandle());
|
mLoaderCursor.allUsers.put(0, Process.myUserHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,26 +117,31 @@ public class LoaderCursorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAppShortcutInfo_dontAllowMissing_validComponent() {
|
public void getAppShortcutInfo_dontAllowMissing_validComponent() throws Exception {
|
||||||
|
ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_PACKAGE);
|
||||||
|
shadowOf(mContext.getPackageManager()).addActivityIfNotPresent(cn);
|
||||||
|
|
||||||
initCursor(ITEM_TYPE_APPLICATION, "");
|
initCursor(ITEM_TYPE_APPLICATION, "");
|
||||||
assertTrue(mLoaderCursor.moveToNext());
|
assertTrue(mLoaderCursor.moveToNext());
|
||||||
|
|
||||||
ComponentName cn = mLauncherApps.getActivityList(null, mLoaderCursor.user)
|
WorkspaceItemInfo info = Executors.MODEL_EXECUTOR.submit(() ->
|
||||||
.get(0).getComponentName();
|
mLoaderCursor.getAppShortcutInfo(
|
||||||
WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
|
new Intent().setComponent(cn), false /* allowMissingTarget */, true))
|
||||||
new Intent().setComponent(cn), false /* allowMissingTarget */, true);
|
.get();
|
||||||
assertNotNull(info);
|
assertNotNull(info);
|
||||||
assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
|
assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAppShortcutInfo_allowMissing_invalidComponent() {
|
public void getAppShortcutInfo_allowMissing_invalidComponent() throws Exception {
|
||||||
initCursor(ITEM_TYPE_APPLICATION, "");
|
initCursor(ITEM_TYPE_APPLICATION, "");
|
||||||
assertTrue(mLoaderCursor.moveToNext());
|
assertTrue(mLoaderCursor.moveToNext());
|
||||||
|
|
||||||
ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
|
ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
|
||||||
WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
|
WorkspaceItemInfo info = Executors.MODEL_EXECUTOR.submit(() ->
|
||||||
new Intent().setComponent(cn), true /* allowMissingTarget */, true);
|
mLoaderCursor.getAppShortcutInfo(
|
||||||
|
new Intent().setComponent(cn), true /* allowMissingTarget */, true))
|
||||||
|
.get();
|
||||||
assertNotNull(info);
|
assertNotNull(info);
|
||||||
assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
|
assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
|
||||||
}
|
}
|
||||||
|
@ -138,11 +151,8 @@ public class LoaderCursorTest {
|
||||||
initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut");
|
initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut");
|
||||||
assertTrue(mLoaderCursor.moveToNext());
|
assertTrue(mLoaderCursor.moveToNext());
|
||||||
|
|
||||||
Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
|
|
||||||
when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
|
|
||||||
.thenReturn(BitmapInfo.fromBitmap(icon));
|
|
||||||
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
|
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
|
||||||
assertEquals(icon, info.bitmap.icon);
|
assertTrue(mApp.getIconCache().isDefaultIcon(info.bitmap, info.user));
|
||||||
assertEquals("my-shortcut", info.title);
|
assertEquals("my-shortcut", info.title);
|
||||||
assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
|
assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
|
||||||
}
|
}
|
|
@ -6,11 +6,14 @@ import com.android.launcher3.ItemInfo;
|
||||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||||
import com.android.launcher3.WorkspaceItemInfo;
|
import com.android.launcher3.WorkspaceItemInfo;
|
||||||
import com.android.launcher3.pm.PackageInstallInfo;
|
import com.android.launcher3.pm.PackageInstallInfo;
|
||||||
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.annotation.LooperMode;
|
||||||
|
import org.robolectric.annotation.LooperMode.Mode;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -18,12 +21,16 @@ import java.util.HashSet;
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PackageInstallStateChangedTask}
|
* Tests for {@link PackageInstallStateChangedTask}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestCase {
|
@LooperMode(Mode.PAUSED)
|
||||||
|
public class PackageInstallStateChangedTaskTest {
|
||||||
|
|
||||||
|
private LauncherModelHelper mModelHelper;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void initData() throws Exception {
|
public void setup() throws Exception {
|
||||||
initializeData("/package_install_state_change_task_data.txt");
|
mModelHelper = new LauncherModelHelper();
|
||||||
|
mModelHelper.initializeData("/package_install_state_change_task_data.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
private PackageInstallStateChangedTask newTask(String pkg, int progress) {
|
private PackageInstallStateChangedTask newTask(String pkg, int progress) {
|
||||||
|
@ -35,7 +42,7 @@ public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestC
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionUpdate_ignore_installed() throws Exception {
|
public void testSessionUpdate_ignore_installed() throws Exception {
|
||||||
executeTaskForTest(newTask("app1", 30));
|
mModelHelper.executeTaskForTest(newTask("app1", 30));
|
||||||
|
|
||||||
// No shortcuts were updated
|
// No shortcuts were updated
|
||||||
verifyProgressUpdate(0);
|
verifyProgressUpdate(0);
|
||||||
|
@ -43,21 +50,21 @@ public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestC
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionUpdate_shortcuts_updated() throws Exception {
|
public void testSessionUpdate_shortcuts_updated() throws Exception {
|
||||||
executeTaskForTest(newTask("app3", 30));
|
mModelHelper.executeTaskForTest(newTask("app3", 30));
|
||||||
|
|
||||||
verifyProgressUpdate(30, 5, 6, 7);
|
verifyProgressUpdate(30, 5, 6, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionUpdate_widgets_updated() throws Exception {
|
public void testSessionUpdate_widgets_updated() throws Exception {
|
||||||
executeTaskForTest(newTask("app4", 30));
|
mModelHelper.executeTaskForTest(newTask("app4", 30));
|
||||||
|
|
||||||
verifyProgressUpdate(30, 8, 9);
|
verifyProgressUpdate(30, 8, 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyProgressUpdate(int progress, Integer... idsUpdated) {
|
private void verifyProgressUpdate(int progress, Integer... idsUpdated) {
|
||||||
HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
|
HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
|
||||||
for (ItemInfo info : bgDataModel.itemsIdMap) {
|
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
|
||||||
if (info instanceof WorkspaceItemInfo) {
|
if (info instanceof WorkspaceItemInfo) {
|
||||||
assertEquals(updates.contains(info.id) ? progress: 0,
|
assertEquals(updates.contains(info.id) ? progress: 0,
|
||||||
((WorkspaceItemInfo) info).getInstallProgress());
|
((WorkspaceItemInfo) info).getInstallProgress());
|
||||||
|
|
|
@ -27,9 +27,10 @@ import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
|
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -39,7 +40,7 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
* Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
|
* Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class PopupPopulatorTest {
|
public class PopupPopulatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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.provider;
|
package com.android.launcher3.provider;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -6,21 +21,18 @@ import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.filters.MediumTest;
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.launcher3.LauncherProvider.DatabaseHelper;
|
import com.android.launcher3.LauncherProvider.DatabaseHelper;
|
||||||
import com.android.launcher3.LauncherSettings.Favorites;
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link RestoreDbTask}
|
* Tests for {@link RestoreDbTask}
|
||||||
*/
|
*/
|
||||||
@MediumTest
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class RestoreDbTaskTest {
|
public class RestoreDbTaskTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -83,7 +95,7 @@ public class RestoreDbTaskTest {
|
||||||
private final long mProfileId;
|
private final long mProfileId;
|
||||||
|
|
||||||
MyDatabaseHelper(long profileId) {
|
MyDatabaseHelper(long profileId) {
|
||||||
super(InstrumentationRegistry.getContext(), null);
|
super(RuntimeEnvironment.application, null);
|
||||||
mProfileId = profileId;
|
mProfileId = profileId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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.shadows;
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager;
|
||||||
|
import android.appwidget.AppWidgetProviderInfo;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
|
||||||
|
import org.robolectric.annotation.Implementation;
|
||||||
|
import org.robolectric.annotation.Implements;
|
||||||
|
import org.robolectric.shadows.ShadowAppWidgetManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link ShadowAppWidgetManager} with missing shadow methods
|
||||||
|
*/
|
||||||
|
@Implements(value = AppWidgetManager.class)
|
||||||
|
public class LShadowAppWidgetManager extends ShadowAppWidgetManager {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<AppWidgetProviderInfo> getInstalledProviders() {
|
||||||
|
return getInstalledProvidersForProfile(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public List<AppWidgetProviderInfo> getInstalledProvidersForProfile(UserHandle profile) {
|
||||||
|
UserHandle user = profile == null ? Process.myUserHandle() : profile;
|
||||||
|
return super.getInstalledProviders().stream().filter(
|
||||||
|
info -> user.equals(info.getProfile())).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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.shadows;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
|
||||||
|
import org.robolectric.annotation.Implementation;
|
||||||
|
import org.robolectric.annotation.Implements;
|
||||||
|
import org.robolectric.shadows.ShadowBitmap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link ShadowBitmap} with missing shadow methods
|
||||||
|
*/
|
||||||
|
@Implements(value = Bitmap.class)
|
||||||
|
public class LShadowBitmap extends ShadowBitmap {
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
protected Bitmap extractAlpha(Paint paint, int[] offsetXY) {
|
||||||
|
return extractAlpha();
|
||||||
|
}
|
||||||
|
}
|
|
@ -77,7 +77,7 @@ public class LShadowLauncherApps extends ShadowLauncherApps {
|
||||||
protected LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
|
protected LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
|
||||||
ResolveInfo ri = RuntimeEnvironment.application.getPackageManager()
|
ResolveInfo ri = RuntimeEnvironment.application.getPackageManager()
|
||||||
.resolveActivity(intent, 0);
|
.resolveActivity(intent, 0);
|
||||||
return getLauncherActivityInfo(ri.activityInfo);
|
return ri == null ? null : getLauncherActivityInfo(ri.activityInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LauncherActivityInfo getLauncherActivityInfo(ActivityInfo activityInfo) {
|
public LauncherActivityInfo getLauncherActivityInfo(ActivityInfo activityInfo) {
|
||||||
|
|
|
@ -18,25 +18,16 @@ package com.android.launcher3.shadows;
|
||||||
|
|
||||||
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
|
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
|
||||||
|
|
||||||
import static org.robolectric.shadow.api.Shadow.invokeConstructor;
|
import static org.robolectric.shadow.api.Shadow.directlyOn;
|
||||||
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
|
|
||||||
import static org.robolectric.util.ReflectionHelpers.setField;
|
import static org.robolectric.util.ReflectionHelpers.setField;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
|
||||||
|
|
||||||
import com.android.launcher3.util.Executors;
|
|
||||||
import com.android.launcher3.util.LooperExecutor;
|
import com.android.launcher3.util.LooperExecutor;
|
||||||
|
|
||||||
import org.robolectric.annotation.Implementation;
|
import org.robolectric.annotation.Implementation;
|
||||||
import org.robolectric.annotation.Implements;
|
import org.robolectric.annotation.Implements;
|
||||||
import org.robolectric.annotation.RealObject;
|
import org.robolectric.annotation.RealObject;
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
|
* Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
|
||||||
|
@ -44,25 +35,18 @@ import java.util.WeakHashMap;
|
||||||
@Implements(value = LooperExecutor.class, isInAndroidSdk = false)
|
@Implements(value = LooperExecutor.class, isInAndroidSdk = false)
|
||||||
public class ShadowLooperExecutor {
|
public class ShadowLooperExecutor {
|
||||||
|
|
||||||
// Keep reference to all created Loopers so they can be torn down after test
|
@RealObject private LooperExecutor mRealExecutor;
|
||||||
private static Set<LooperExecutor> executors =
|
|
||||||
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
|
|
||||||
|
|
||||||
@RealObject private LooperExecutor realExecutor;
|
|
||||||
|
|
||||||
@Implementation
|
@Implementation
|
||||||
protected void __constructor__(Looper looper) {
|
protected Handler getHandler() {
|
||||||
invokeConstructor(LooperExecutor.class, realExecutor, from(Looper.class, looper));
|
Handler handler = directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
|
||||||
executors.add(realExecutor);
|
Thread thread = handler.getLooper().getThread();
|
||||||
}
|
if (!thread.isAlive()) {
|
||||||
|
// Robolectric destroys all loopers at the end of every test. Since Launcher maintains
|
||||||
/**
|
// some static threads, they need to be reinitialized in case they were destroyed.
|
||||||
* Re-initializes any executor which may have been reset when a test finished
|
setField(mRealExecutor, "mHandler",
|
||||||
*/
|
new Handler(createAndStartNewLooper(thread.getName())));
|
||||||
public static void reinitializeStaticExecutors() {
|
|
||||||
for (LooperExecutor executor : new ArrayList<>(executors)) {
|
|
||||||
setField(executor, "mHandler",
|
|
||||||
new Handler(createAndStartNewLooper(executor.getThread().getName())));
|
|
||||||
}
|
}
|
||||||
|
return directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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.shadows;
|
||||||
|
|
||||||
|
import static org.robolectric.shadow.api.Shadow.invokeConstructor;
|
||||||
|
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
|
||||||
|
|
||||||
|
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||||
|
import com.android.launcher3.util.MainThreadInitializedObject.ObjectProvider;
|
||||||
|
|
||||||
|
import org.robolectric.annotation.Implementation;
|
||||||
|
import org.robolectric.annotation.Implements;
|
||||||
|
import org.robolectric.annotation.RealObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shadow for {@link MainThreadInitializedObject} to provide reset functionality for static sObjects
|
||||||
|
*/
|
||||||
|
@Implements(value = MainThreadInitializedObject.class, isInAndroidSdk = false)
|
||||||
|
public class ShadowMainThreadInitializedObject {
|
||||||
|
|
||||||
|
// Keep reference to all created MainThreadInitializedObject so they can be cleared after test
|
||||||
|
private static Set<MainThreadInitializedObject> sObjects =
|
||||||
|
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
|
||||||
|
|
||||||
|
@RealObject private MainThreadInitializedObject mRealObject;
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
protected void __constructor__(ObjectProvider provider) {
|
||||||
|
invokeConstructor(MainThreadInitializedObject.class, mRealObject,
|
||||||
|
from(ObjectProvider.class, provider));
|
||||||
|
sObjects.add(mRealObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all the initialized sObjects to be null
|
||||||
|
*/
|
||||||
|
public static void resetInitializedObjects() {
|
||||||
|
for (MainThreadInitializedObject object : new ArrayList<>(sObjects)) {
|
||||||
|
object.initializeForTesting(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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.shadows;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.android.launcher3.uioverrides.TogglableFlag;
|
||||||
|
import com.android.launcher3.util.LooperExecutor;
|
||||||
|
|
||||||
|
import org.robolectric.annotation.Implementation;
|
||||||
|
import org.robolectric.annotation.Implements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
|
||||||
|
*/
|
||||||
|
@Implements(value = TogglableFlag.class, isInAndroidSdk = false)
|
||||||
|
public class ShadowTogglableFlag {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock change listener as it uses internal system classes not available to robolectric
|
||||||
|
*/
|
||||||
|
@Implementation
|
||||||
|
protected void addChangeListener(Context context, Runnable r) { }
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package com.android.launcher3.util;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
@ -11,7 +10,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link GridOccupancy}
|
* Unit tests for {@link GridOccupancy}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class GridOccupancyTest {
|
public class GridOccupancyTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -19,12 +19,11 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Robolectric unit tests for {@link IntArray}
|
* Robolectric unit tests for {@link IntArray}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class IntArrayTest {
|
public class IntArrayTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -20,8 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
@ -29,7 +27,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
/**
|
/**
|
||||||
* Robolectric unit tests for {@link IntSet}
|
* Robolectric unit tests for {@link IntSet}
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
public class IntSetTest {
|
public class IntSetTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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.util;
|
||||||
|
|
||||||
|
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.atLeast;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.android.launcher3.AppInfo;
|
||||||
|
import com.android.launcher3.ItemInfo;
|
||||||
|
import com.android.launcher3.LauncherAppState;
|
||||||
|
import com.android.launcher3.LauncherModel;
|
||||||
|
import com.android.launcher3.LauncherModel.ModelUpdateTask;
|
||||||
|
import com.android.launcher3.LauncherProvider;
|
||||||
|
import com.android.launcher3.LauncherSettings;
|
||||||
|
import com.android.launcher3.model.AllAppsList;
|
||||||
|
import com.android.launcher3.model.BgDataModel;
|
||||||
|
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.shadows.ShadowContentResolver;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to help manage Launcher Model and related objects for test.
|
||||||
|
*/
|
||||||
|
public class LauncherModelHelper {
|
||||||
|
|
||||||
|
public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||||
|
public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||||
|
|
||||||
|
public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||||
|
public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
||||||
|
public static final int NO__ICON = -1;
|
||||||
|
public static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
|
||||||
|
|
||||||
|
private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>();
|
||||||
|
public final TestLauncherProvider provider;
|
||||||
|
|
||||||
|
private BgDataModel mDataModel;
|
||||||
|
private AllAppsList mAllAppsList;
|
||||||
|
|
||||||
|
public LauncherModelHelper() {
|
||||||
|
provider = Robolectric.setupContentProvider(TestLauncherProvider.class);
|
||||||
|
ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LauncherModel getModel() {
|
||||||
|
return LauncherAppState.getInstance(RuntimeEnvironment.application).getModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized BgDataModel getBgDataModel() {
|
||||||
|
if (mDataModel == null) {
|
||||||
|
mDataModel = ReflectionHelpers.getField(getModel(), "mBgDataModel");
|
||||||
|
}
|
||||||
|
return mDataModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized AllAppsList getAllAppsList() {
|
||||||
|
if (mAllAppsList == null) {
|
||||||
|
mAllAppsList = ReflectionHelpers.getField(getModel(), "mBgAllAppsList");
|
||||||
|
}
|
||||||
|
return mAllAppsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronously executes the task and returns all the UI callbacks posted.
|
||||||
|
*/
|
||||||
|
public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
|
||||||
|
LauncherModel model = getModel();
|
||||||
|
if (!model.isModelLoaded()) {
|
||||||
|
ReflectionHelpers.setField(model, "mModelLoaded", true);
|
||||||
|
}
|
||||||
|
Executor mockExecutor = mock(Executor.class);
|
||||||
|
model.enqueueModelUpdateTask(new ModelUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
|
||||||
|
AllAppsList allAppsList, Executor uiExecutor) {
|
||||||
|
task.init(app, model, dataModel, allAppsList, mockExecutor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
MODEL_EXECUTOR.submit(() -> null).get();
|
||||||
|
|
||||||
|
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
||||||
|
verify(mockExecutor, atLeast(0)).execute(captor.capture());
|
||||||
|
return captor.getAllValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronously executes a task on the model
|
||||||
|
*/
|
||||||
|
public <T> T executeSimpleTask(Function<BgDataModel, T> task) throws Exception {
|
||||||
|
BgDataModel dataModel = getBgDataModel();
|
||||||
|
return MODEL_EXECUTOR.submit(() -> task.apply(dataModel)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes mock data for the test.
|
||||||
|
*/
|
||||||
|
public void initializeData(String resourceName) throws Exception {
|
||||||
|
Context targetContext = RuntimeEnvironment.application;
|
||||||
|
BgDataModel bgDataModel = getBgDataModel();
|
||||||
|
AllAppsList allAppsList = getAllAppsList();
|
||||||
|
|
||||||
|
MODEL_EXECUTOR.submit(() -> {
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||||
|
this.getClass().getResourceAsStream(resourceName)))) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception {
|
||||||
|
HashMap<String, Field> cache = mFieldCache.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();
|
||||||
|
}
|
||||||
|
mFieldCache.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a dummy item in the DB.
|
||||||
|
* @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
|
||||||
|
* folder (where the type represents the number of items in the folder).
|
||||||
|
*/
|
||||||
|
public int addItem(int type, int screen, int container, int x, int y) {
|
||||||
|
Context context = RuntimeEnvironment.application;
|
||||||
|
int id = LauncherSettings.Settings.call(context.getContentResolver(),
|
||||||
|
LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
|
||||||
|
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(LauncherSettings.Favorites._ID, id);
|
||||||
|
values.put(LauncherSettings.Favorites.CONTAINER, container);
|
||||||
|
values.put(LauncherSettings.Favorites.SCREEN, screen);
|
||||||
|
values.put(LauncherSettings.Favorites.CELLX, x);
|
||||||
|
values.put(LauncherSettings.Favorites.CELLY, y);
|
||||||
|
values.put(LauncherSettings.Favorites.SPANX, 1);
|
||||||
|
values.put(LauncherSettings.Favorites.SPANY, 1);
|
||||||
|
|
||||||
|
if (type == APP_ICON || type == SHORTCUT) {
|
||||||
|
values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
|
||||||
|
values.put(LauncherSettings.Favorites.INTENT,
|
||||||
|
new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0));
|
||||||
|
} else {
|
||||||
|
values.put(LauncherSettings.Favorites.ITEM_TYPE,
|
||||||
|
LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
|
||||||
|
// Add folder items.
|
||||||
|
for (int i = 0; i < type; i++) {
|
||||||
|
addItem(APP_ICON, 0, id, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.getContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[][][] createGrid(int[][][] typeArray) {
|
||||||
|
return createGrid(typeArray, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the DB with dummy elements to represent the provided grid structure.
|
||||||
|
* @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
|
||||||
|
* type definitions. The first dimension represents the screens and the next
|
||||||
|
* two represent the workspace grid.
|
||||||
|
* @param startScreen First screen id from where the icons will be added.
|
||||||
|
* @return the same grid representation where each entry is the corresponding item id.
|
||||||
|
*/
|
||||||
|
public int[][][] createGrid(int[][][] typeArray, int startScreen) {
|
||||||
|
Context context = RuntimeEnvironment.application;
|
||||||
|
LauncherSettings.Settings.call(context.getContentResolver(),
|
||||||
|
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
||||||
|
int[][][] ids = new int[typeArray.length][][];
|
||||||
|
|
||||||
|
for (int i = 0; i < typeArray.length; i++) {
|
||||||
|
// Add screen to DB
|
||||||
|
int screenId = startScreen + i;
|
||||||
|
|
||||||
|
// Keep the screen id counter up to date
|
||||||
|
LauncherSettings.Settings.call(context.getContentResolver(),
|
||||||
|
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
||||||
|
|
||||||
|
ids[i] = new int[typeArray[i].length][];
|
||||||
|
for (int y = 0; y < typeArray[i].length; y++) {
|
||||||
|
ids[i][y] = new int[typeArray[i][y].length];
|
||||||
|
for (int x = 0; x < typeArray[i][y].length; x++) {
|
||||||
|
if (typeArray[i][y][x] < 0) {
|
||||||
|
// Empty cell
|
||||||
|
ids[i][y][x] = -1;
|
||||||
|
} else {
|
||||||
|
ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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.util;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import com.android.launcher3.shadows.LShadowAppWidgetManager;
|
||||||
|
import com.android.launcher3.shadows.LShadowBitmap;
|
||||||
|
import com.android.launcher3.shadows.LShadowLauncherApps;
|
||||||
|
import com.android.launcher3.shadows.LShadowUserManager;
|
||||||
|
import com.android.launcher3.shadows.ShadowLooperExecutor;
|
||||||
|
import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
|
||||||
|
import com.android.launcher3.shadows.ShadowTogglableFlag;
|
||||||
|
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
|
||||||
|
|
||||||
|
import org.junit.runners.model.InitializationError;
|
||||||
|
import org.robolectric.DefaultTestLifecycle;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.TestLifecycle;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadows.ShadowLog;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test runner with Launcher specific configurations
|
||||||
|
*/
|
||||||
|
public class LauncherRoboTestRunner extends RobolectricTestRunner {
|
||||||
|
|
||||||
|
private static final Class<?>[] SHADOWS = new Class<?>[] {
|
||||||
|
LShadowAppWidgetManager.class,
|
||||||
|
LShadowUserManager.class,
|
||||||
|
LShadowLauncherApps.class,
|
||||||
|
LShadowBitmap.class,
|
||||||
|
|
||||||
|
ShadowLooperExecutor.class,
|
||||||
|
ShadowMainThreadInitializedObject.class,
|
||||||
|
ShadowTogglableFlag.class,
|
||||||
|
};
|
||||||
|
|
||||||
|
public LauncherRoboTestRunner(Class<?> testClass) throws InitializationError {
|
||||||
|
super(testClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Config buildGlobalConfig() {
|
||||||
|
return new Config.Builder().setShadows(SHADOWS).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
protected Class<? extends TestLifecycle> getTestLifecycleClass() {
|
||||||
|
return LauncherTestLifecycle.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LauncherTestLifecycle extends DefaultTestLifecycle {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTest(Method method) {
|
||||||
|
super.beforeTest(method);
|
||||||
|
ShadowLog.stream = System.out;
|
||||||
|
|
||||||
|
// Disable plugins
|
||||||
|
PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTest(Method method) {
|
||||||
|
super.afterTest(method);
|
||||||
|
|
||||||
|
ShadowLog.stream = null;
|
||||||
|
ShadowMainThreadInitializedObject.resetInitializedObjects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,16 +19,15 @@ import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Matchers.isNull;
|
import static org.mockito.Matchers.isNull;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
|
||||||
import android.appwidget.AppWidgetProviderInfo;
|
import android.appwidget.AppWidgetProviderInfo;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.filters.SmallTest;
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.launcher3.InvariantDeviceProfile;
|
import com.android.launcher3.InvariantDeviceProfile;
|
||||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||||
|
@ -37,19 +36,21 @@ import com.android.launcher3.icons.BitmapInfo;
|
||||||
import com.android.launcher3.icons.IconCache;
|
import com.android.launcher3.icons.IconCache;
|
||||||
import com.android.launcher3.model.PackageItemInfo;
|
import com.android.launcher3.model.PackageItemInfo;
|
||||||
import com.android.launcher3.model.WidgetItem;
|
import com.android.launcher3.model.WidgetItem;
|
||||||
import com.android.launcher3.util.MultiHashMap;
|
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.shadows.ShadowPackageManager;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.Collections;
|
||||||
|
|
||||||
@SmallTest
|
@RunWith(LauncherRoboTestRunner.class)
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class WidgetsListAdapterTest {
|
public class WidgetsListAdapterTest {
|
||||||
|
|
||||||
@Mock private LayoutInflater mMockLayoutInflater;
|
@Mock private LayoutInflater mMockLayoutInflater;
|
||||||
|
@ -64,7 +65,7 @@ public class WidgetsListAdapterTest {
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mContext = InstrumentationRegistry.getTargetContext();
|
mContext = RuntimeEnvironment.application;
|
||||||
mTestProfile = new InvariantDeviceProfile();
|
mTestProfile = new InvariantDeviceProfile();
|
||||||
mTestProfile.numRows = 5;
|
mTestProfile.numRows = 5;
|
||||||
mTestProfile.numColumns = 5;
|
mTestProfile.numColumns = 5;
|
||||||
|
@ -121,15 +122,19 @@ public class WidgetsListAdapterTest {
|
||||||
/**
|
/**
|
||||||
* Helper method to generate the sample widget model map that can be used for the tests
|
* Helper method to generate the sample widget model map that can be used for the tests
|
||||||
* @param num the number of WidgetItem the map should contain
|
* @param num the number of WidgetItem the map should contain
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private ArrayList<WidgetListRowEntry> generateSampleMap(int num) {
|
private ArrayList<WidgetListRowEntry> generateSampleMap(int num) {
|
||||||
ArrayList<WidgetListRowEntry> result = new ArrayList<>();
|
ArrayList<WidgetListRowEntry> result = new ArrayList<>();
|
||||||
if (num <= 0) return result;
|
if (num <= 0) return result;
|
||||||
|
ShadowPackageManager spm = shadowOf(mContext.getPackageManager());
|
||||||
|
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
ComponentName cn = new ComponentName("com.dummy.apk" + i, "DummyWidet");
|
||||||
|
|
||||||
|
AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
|
||||||
|
widgetInfo.provider = cn;
|
||||||
|
ReflectionHelpers.setField(widgetInfo, "providerInfo", spm.addReceiverIfNotPresent(cn));
|
||||||
|
|
||||||
MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
|
|
||||||
WidgetManagerHelper widgetManager = new WidgetManagerHelper(mContext);
|
|
||||||
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(null)) {
|
|
||||||
WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
|
WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
|
||||||
.fromProviderInfo(mContext, widgetInfo), mTestProfile, mIconCache);
|
.fromProviderInfo(mContext, widgetInfo), mTestProfile, mIconCache);
|
||||||
|
|
||||||
|
@ -137,13 +142,8 @@ public class WidgetsListAdapterTest {
|
||||||
pInfo.title = pInfo.packageName;
|
pInfo.title = pInfo.packageName;
|
||||||
pInfo.user = wi.user;
|
pInfo.user = wi.user;
|
||||||
pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
|
pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
|
||||||
newMap.addToList(pInfo, wi);
|
|
||||||
if (newMap.size() == num) {
|
result.add(new WidgetListRowEntry(pInfo, new ArrayList<>(Collections.singleton(wi))));
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : newMap.entrySet()) {
|
|
||||||
result.add(new WidgetListRowEntry(entry.getKey(), entry.getValue()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
|
@ -108,14 +108,14 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
|
||||||
* All the static data should be accessed on the background thread, A lock should be acquired
|
* All the static data should be accessed on the background thread, A lock should be acquired
|
||||||
* on this object when accessing any data from this model.
|
* on this object when accessing any data from this model.
|
||||||
*/
|
*/
|
||||||
static final BgDataModel sBgDataModel = new BgDataModel();
|
private final BgDataModel mBgDataModel = new BgDataModel();
|
||||||
|
|
||||||
// Runnable to check if the shortcuts permission has changed.
|
// Runnable to check if the shortcuts permission has changed.
|
||||||
private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
|
private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (mModelLoaded && hasShortcutsPermission(mApp.getContext())
|
if (mModelLoaded && hasShortcutsPermission(mApp.getContext())
|
||||||
!= sBgDataModel.hasShortcutHostPermission) {
|
!= mBgDataModel.hasShortcutHostPermission) {
|
||||||
forceReload();
|
forceReload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) {
|
public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) {
|
||||||
return new ModelWriter(mApp.getContext(), this, sBgDataModel,
|
return new ModelWriter(mApp.getContext(), this, mBgDataModel,
|
||||||
hasVerticalHotseat, verifyChanges);
|
hasVerticalHotseat, verifyChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
|
||||||
|
|
||||||
// If there is already one running, tell it to stop.
|
// If there is already one running, tell it to stop.
|
||||||
stopLoader();
|
stopLoader();
|
||||||
LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel,
|
LoaderResults loaderResults = new LoaderResults(mApp, mBgDataModel,
|
||||||
mBgAllAppsList, synchronousBindPage, mCallbacks);
|
mBgAllAppsList, synchronousBindPage, mCallbacks);
|
||||||
if (mModelLoaded && !mIsLoaderTaskRunning) {
|
if (mModelLoaded && !mIsLoaderTaskRunning) {
|
||||||
// Divide the set of loaded items into those that we are binding synchronously,
|
// Divide the set of loaded items into those that we are binding synchronously,
|
||||||
|
@ -339,7 +339,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
|
||||||
public void startLoaderForResults(LoaderResults results) {
|
public void startLoaderForResults(LoaderResults results) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
stopLoader();
|
stopLoader();
|
||||||
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
|
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, mBgDataModel, results);
|
||||||
|
|
||||||
// Always post the loader task, instead of running directly (even on same thread) so
|
// Always post the loader task, instead of running directly (even on same thread) so
|
||||||
// that we exit any nested synchronized blocks
|
// that we exit any nested synchronized blocks
|
||||||
|
@ -487,7 +487,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enqueueModelUpdateTask(ModelUpdateTask task) {
|
public void enqueueModelUpdateTask(ModelUpdateTask task) {
|
||||||
task.init(mApp, this, sBgDataModel, mBgAllAppsList, MAIN_EXECUTOR);
|
task.init(mApp, this, mBgDataModel, mBgAllAppsList, MAIN_EXECUTOR);
|
||||||
MODEL_EXECUTOR.execute(task);
|
MODEL_EXECUTOR.execute(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,7 +558,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
|
||||||
+ " componentName=" + info.componentName.getPackageName());
|
+ " componentName=" + info.componentName.getPackageName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sBgDataModel.dump(prefix, fd, writer, args);
|
mBgDataModel.dump(prefix, fd, writer, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Callbacks getCallback() {
|
public Callbacks getCallback() {
|
||||||
|
|
|
@ -19,7 +19,6 @@ import android.os.HandlerThread;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -36,9 +35,9 @@ public class Executors {
|
||||||
private static final int KEEP_ALIVE = 1;
|
private static final int KEEP_ALIVE = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link Executor} to be used with async task with no limit on the queue size.
|
* An {@link ThreadPoolExecutor} to be used with async task with no limit on the queue size.
|
||||||
*/
|
*/
|
||||||
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
|
public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
|
||||||
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
|
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
|
||||||
TimeUnit.SECONDS, new LinkedBlockingQueue<>());
|
TimeUnit.SECONDS, new LinkedBlockingQueue<>());
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,10 @@ public class LooperExecutor extends AbstractExecutorService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Runnable runnable) {
|
public void execute(Runnable runnable) {
|
||||||
if (mHandler.getLooper() == Looper.myLooper()) {
|
if (getHandler().getLooper() == Looper.myLooper()) {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
} else {
|
} else {
|
||||||
mHandler.post(runnable);
|
getHandler().post(runnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ public class LooperExecutor extends AbstractExecutorService {
|
||||||
* Same as execute, but never runs the action inline.
|
* Same as execute, but never runs the action inline.
|
||||||
*/
|
*/
|
||||||
public void post(Runnable runnable) {
|
public void post(Runnable runnable) {
|
||||||
mHandler.post(runnable);
|
getHandler().post(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,14 +96,14 @@ public class LooperExecutor extends AbstractExecutorService {
|
||||||
* Returns the thread for this executor
|
* Returns the thread for this executor
|
||||||
*/
|
*/
|
||||||
public Thread getThread() {
|
public Thread getThread() {
|
||||||
return mHandler.getLooper().getThread();
|
return getHandler().getLooper().getThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the looper for this executor
|
* Returns the looper for this executor
|
||||||
*/
|
*/
|
||||||
public Looper getLooper() {
|
public Looper getLooper() {
|
||||||
return mHandler.getLooper();
|
return getHandler().getLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue