include reboelctric test for restore
Bug: 141472083
Change-Id: Ibee123b01c74316d493d3aa229acf4a4f815f60b
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
index 86a6e8c..6059981 100644
--- a/robolectric_tests/Android.mk
+++ b/robolectric_tests/Android.mk
@@ -29,7 +29,7 @@
mockito-robolectric-prebuilt \
truth-prebuilt
LOCAL_JAVA_LIBRARIES := \
- platform-robolectric-4.3-prebuilt
+ platform-robolectric-4.3.1-prebuilt
LOCAL_JAVA_RESOURCE_DIRS := resources config
@@ -56,4 +56,4 @@
LOCAL_ROBOTEST_TIMEOUT := 36000
-include prebuilts/misc/common/robolectric/4.3/run_robotests.mk
+include prebuilts/misc/common/robolectric/4.3.1/run_robotests.mk
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
index 932b01b..3d78689 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/config/robolectric.properties
@@ -1 +1 @@
-sdk=28
+sdk=29
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 6223760..7072adf 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -16,14 +16,32 @@
package com.android.launcher3.model;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
+
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.addTableToDb;
+import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
+import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
+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.assertTrue;
+import static org.robolectric.util.ReflectionHelpers.setField;
+import android.app.backup.BackupManager;
+import android.content.pm.PackageInstaller;
+import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.os.UserHandle;
+import android.os.UserManager;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.shadows.LShadowBackupManager;
+import com.android.launcher3.shadows.LShadowUserManager;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
@@ -32,6 +50,7 @@
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
+import org.robolectric.shadow.api.Shadow;
/**
* Tests to verify backup and restore flow.
@@ -40,18 +59,123 @@
@LooperMode(LooperMode.Mode.PAUSED)
public class BackupRestoreTest {
+ private static final long MY_OLD_PROFILE_ID = 1;
+ private static final long MY_PROFILE_ID = 0;
+ private static final long OLD_WORK_PROFILE_ID = 11;
+ private static final int WORK_PROFILE_ID = 10;
+
+ private static final int SYSTEM_USER = 0;
+ private static final int FLAG_SYSTEM = 0x00000800;
+ private static final int FLAG_PROFILE = 0x00001000;
+
+ private LShadowUserManager mUserManager;
+ private BackupManager mBackupManager;
private LauncherModelHelper mModelHelper;
private SQLiteDatabase mDb;
+ private InvariantDeviceProfile mIdp;
+ private UserHandle mMainProfileUser;
+ private UserHandle mWorkProfileUser;
@Before
public void setUp() {
+ setupUserManager();
+ setupBackupManager();
mModelHelper = new LauncherModelHelper();
RestoreDbTask.setPending(RuntimeEnvironment.application, true);
mDb = mModelHelper.provider.getDb();
+ mIdp = InvariantDeviceProfile.INSTANCE.get(RuntimeEnvironment.application);
+ }
+
+ private void setupUserManager() {
+ final UserManager userManager = RuntimeEnvironment.application.getSystemService(
+ UserManager.class);
+ mUserManager = Shadow.extract(userManager);
+ // sign in to primary user
+ mMainProfileUser = mUserManager.addUser(SYSTEM_USER, "me", FLAG_SYSTEM);
+ // sign in to work profile
+ mWorkProfileUser = mUserManager.addUser(WORK_PROFILE_ID, "work", FLAG_PROFILE);
+ }
+
+ private void setupBackupManager() {
+ mBackupManager = new BackupManager(RuntimeEnvironment.application);
+ final LShadowBackupManager bm = Shadow.extract(mBackupManager);
+ bm.addProfile(MY_OLD_PROFILE_ID, mMainProfileUser);
+ bm.addProfile(OLD_WORK_PROFILE_ID, mWorkProfileUser);
}
@Test
public void testOnCreateDbIfNotExists_CreatesBackup() {
assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
}
+
+ @Test
+ public void testOnRestoreSessionWithValidCondition_PerformsRestore() throws Exception {
+ setupBackup();
+ verifyTableIsFilled(BACKUP_TABLE_NAME, false);
+ verifyTableIsEmpty(TABLE_NAME);
+ createRestoreSession();
+ verifyTableIsFilled(TABLE_NAME, true);
+ }
+
+ private void setupBackup() {
+ createTableUsingOldProfileId();
+ // setup grid for main user on first screen
+ mModelHelper.createGrid(new int[][][]{{
+ { APP_ICON, APP_ICON, SHORTCUT, SHORTCUT},
+ { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
+ { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
+ { APP_ICON, SHORTCUT, SHORTCUT, APP_ICON},
+ }}, 1, MY_OLD_PROFILE_ID);
+ // setup grid for work profile on second screen
+ mModelHelper.createGrid(new int[][][]{{
+ { NO__ICON, APP_ICON, SHORTCUT, SHORTCUT},
+ { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
+ { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
+ { APP_ICON, SHORTCUT, SHORTCUT, NO__ICON},
+ }}, 2, OLD_WORK_PROFILE_ID);
+ // simulates the creation of backup upon restore
+ new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numHotseatIcons,
+ mIdp.numColumns, mIdp.numRows).doBackup(
+ MY_OLD_PROFILE_ID, GridBackupTable.OPTION_REQUIRES_SANITIZATION);
+ // reset favorites table
+ createTableUsingOldProfileId();
+ }
+
+ private void verifyTableIsEmpty(String tableName) {
+ assertEquals(0, getCount(mDb, "SELECT * FROM " + tableName));
+ }
+
+ private void verifyTableIsFilled(String tableName, boolean sanitized) {
+ assertEquals(sanitized ? 12 : 13, getCount(mDb,
+ "SELECT * FROM " + tableName + " WHERE profileId = "
+ + (sanitized ? MY_PROFILE_ID : MY_OLD_PROFILE_ID)));
+ assertEquals(10, getCount(mDb, "SELECT * FROM " + tableName + " WHERE profileId = "
+ + (sanitized ? WORK_PROFILE_ID : OLD_WORK_PROFILE_ID)));
+ }
+
+ private void createTableUsingOldProfileId() {
+ // simulates the creation of favorites table on old device
+ dropTable(mDb, TABLE_NAME);
+ addTableToDb(mDb, MY_OLD_PROFILE_ID, false);
+ }
+
+ private void createRestoreSession() throws Exception {
+ final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ final PackageInstaller installer = RuntimeEnvironment.application.getPackageManager()
+ .getPackageInstaller();
+ final int sessionId = installer.createSession(params);
+ final PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
+ setField(info, "installReason", INSTALL_REASON_DEVICE_RESTORE);
+ // TODO: (b/148410677) we should verify the following call instead
+ // InstallSessionHelper.INSTANCE.get(getContext()).restoreDbIfApplicable(info);
+ RestoreDbTask.restoreIfPossible(RuntimeEnvironment.application,
+ mModelHelper.provider.getHelper(), mBackupManager);
+ }
+
+ private static int getCount(SQLiteDatabase db, String sql) {
+ try (Cursor c = db.rawQuery(sql, null)) {
+ return c.getCount();
+ }
+ }
}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java
new file mode 100644
index 0000000..eae0101
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowBackupManager.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 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.app.backup.BackupManager;
+import android.os.UserHandle;
+import android.util.LongSparseArray;
+
+import androidx.annotation.Nullable;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowBackupManager;
+
+/**
+ * Extension of {@link ShadowBackupManager} with missing shadow methods
+ */
+@Implements(value = BackupManager.class)
+public class LShadowBackupManager extends ShadowBackupManager {
+
+ private LongSparseArray<UserHandle> mProfileMapping = new LongSparseArray<>();
+
+ public void addProfile(long userSerial, UserHandle userHandle) {
+ mProfileMapping.put(userSerial, userHandle);
+ }
+
+ @Implementation
+ @Nullable
+ public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+ return mProfileMapping.get(ancestralSerialNumber);
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
index 166e28b..f16ed33 100644
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
@@ -26,6 +26,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -43,6 +44,7 @@
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.stream.Collectors;
/**
@@ -111,6 +113,17 @@
return true;
}
+ @Implementation
+ public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
+ return RuntimeEnvironment.application.getPackageManager().getPackageInstaller()
+ .getAllSessions();
+ }
+
+ @Implementation
+ public void registerPackageInstallerSessionCallback(
+ Executor executor, PackageInstaller.SessionCallback callback) {
+ }
+
@Override
protected List<LauncherActivityInfo> getShortcutConfigActivityList(String packageName,
UserHandle user) {
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index e8b7157..e133cf2 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.util;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static org.mockito.Mockito.atLeast;
@@ -30,6 +31,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
+import android.os.Process;
import android.provider.Settings;
import com.android.launcher3.AppInfo;
@@ -42,6 +44,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.pm.UserCache;
import org.mockito.ArgumentCaptor;
import org.robolectric.Robolectric;
@@ -80,15 +83,17 @@
private static final int DEFAULT_BITMAP_SIZE = 10;
private static final int DEFAULT_GRID_SIZE = 4;
-
private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>();
public final TestLauncherProvider provider;
+ private final long mDefaultProfileId;
private BgDataModel mDataModel;
private AllAppsList mAllAppsList;
public LauncherModelHelper() {
provider = Robolectric.setupContentProvider(TestLauncherProvider.class);
+ mDefaultProfileId = UserCache.INSTANCE.get(RuntimeEnvironment.application)
+ .getSerialNumberForUser(Process.myUserHandle());
ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, provider);
}
@@ -224,12 +229,16 @@
return item;
}
+ public int addItem(int type, int screen, int container, int x, int y) {
+ return addItem(type, screen, container, x, y, mDefaultProfileId);
+ }
+
/**
* 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) {
+ public int addItem(int type, int screen, int container, int x, int y, long profileId) {
Context context = RuntimeEnvironment.application;
int id = LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
@@ -243,6 +252,7 @@
values.put(LauncherSettings.Favorites.CELLY, y);
values.put(LauncherSettings.Favorites.SPANX, 1);
values.put(LauncherSettings.Favorites.SPANY, 1);
+ values.put(LauncherSettings.Favorites.PROFILE_ID, profileId);
if (type == APP_ICON || type == SHORTCUT) {
values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
@@ -253,11 +263,11 @@
LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
// Add folder items.
for (int i = 0; i < type; i++) {
- addItem(APP_ICON, 0, id, 0, 0);
+ addItem(APP_ICON, 0, id, 0, 0, profileId);
}
}
- context.getContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
+ context.getContentResolver().insert(CONTENT_URI, values);
return id;
}
@@ -265,6 +275,15 @@
return createGrid(typeArray, 1);
}
+ public int[][][] createGrid(int[][][] typeArray, int startScreen) {
+ final Context context = RuntimeEnvironment.application;
+ LauncherSettings.Settings.call(context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ LauncherSettings.Settings.call(context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+ return createGrid(typeArray, startScreen, mDefaultProfileId);
+ }
+
/**
* 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
@@ -273,14 +292,9 @@
* @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) {
+ public int[][][] createGrid(int[][][] typeArray, int startScreen, long profileId) {
Context context = RuntimeEnvironment.application;
- LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
int[][][] ids = new int[typeArray.length][][];
-
for (int i = 0; i < typeArray.length; i++) {
// Add screen to DB
int screenId = startScreen + i;
@@ -297,7 +311,8 @@
// Empty cell
ids[i][y][x] = -1;
} else {
- ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
+ ids[i][y][x] = addItem(
+ typeArray[i][y][x], screenId, DESKTOP, x, y, profileId);
}
}
}
@@ -357,5 +372,9 @@
createDbIfNotExists();
return mOpenHelper.getWritableDatabase();
}
+
+ public DatabaseHelper getHelper() {
+ return mOpenHelper;
+ }
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java b/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java
index b8fff9c..6277c66 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java
@@ -18,12 +18,13 @@
import static org.mockito.Mockito.mock;
import com.android.launcher3.shadows.LShadowAppWidgetManager;
+import com.android.launcher3.shadows.LShadowBackupManager;
import com.android.launcher3.shadows.LShadowBitmap;
import com.android.launcher3.shadows.LShadowLauncherApps;
import com.android.launcher3.shadows.LShadowUserManager;
+import com.android.launcher3.shadows.ShadowDeviceFlag;
import com.android.launcher3.shadows.ShadowLooperExecutor;
import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
-import com.android.launcher3.shadows.ShadowDeviceFlag;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import org.junit.runners.model.InitializationError;
@@ -47,7 +48,7 @@
LShadowUserManager.class,
LShadowLauncherApps.class,
LShadowBitmap.class,
-
+ LShadowBackupManager.class,
ShadowLooperExecutor.class,
ShadowMainThreadInitializedObject.class,
ShadowDeviceFlag.class,