Files
Lawnchair/tests/multivalentTests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
T
Uwais Ashraf c902df4e41 Enable currently functional multivalent tests
Flag: EXEMPT test-only
Bug: 325088619
Bug: 325088638
Bug: 325088328
Test: Presubmit
Test: Gradle robolectric run
Change-Id: I20e77a401ed98c97168bf9c752eacc93c3c545dd
2024-06-22 22:14:30 +00:00

418 lines
17 KiB
Java

/*
* 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;
import static android.os.Process.myUserHandle;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.backup.BackupManager;
import android.appwidget.AppWidgetHost;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.LongSparseArray;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LauncherModelHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.util.Arrays;
import java.util.stream.IntStream;
/**
* Tests for {@link RestoreDbTask}
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RestoreDbTaskTest {
private static final int PER_USER_RANGE = 200000;
private final UserHandle mWorkUser = UserHandle.getUserHandleForUid(PER_USER_RANGE);
private LauncherModelHelper mModelHelper;
private Context mContext;
private RestoreDbTask mTask;
private ModelDbController mMockController;
private SQLiteDatabase mMockDb;
private Cursor mMockCursor;
private LauncherPrefs mPrefs;
private LauncherRestoreEventLogger mMockRestoreEventLogger;
@Before
public void setup() {
mModelHelper = new LauncherModelHelper();
mContext = mModelHelper.sandboxContext;
mTask = new RestoreDbTask();
mMockController = Mockito.mock(ModelDbController.class);
mMockDb = mock(SQLiteDatabase.class);
mMockCursor = mock(Cursor.class);
mPrefs = new LauncherPrefs(mContext);
mMockRestoreEventLogger = mock(LauncherRestoreEventLogger.class);
}
@After
public void teardown() {
mModelHelper.destroy();
LauncherPrefs.get(mContext).removeSync(RESTORE_DEVICE);
}
@Test
public void testGetProfileId() throws Exception {
SQLiteDatabase db = new MyModelDbController(23).getDb();
assertEquals(23, new RestoreDbTask().getDefaultProfileId(db));
}
@Test
public void testMigrateProfileId() throws Exception {
SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < 5; i++) {
ContentValues values = new ContentValues();
values.put(Favorites._ID, i);
values.put(Favorites.TITLE, "item " + i);
db.insert(Favorites.TABLE_NAME, null, values);
}
// Verify item add
assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
new RestoreDbTask().migrateProfileId(db, 42, 33);
// verify data migrated
assertEquals(0, getCount(db, "select * from favorites where profileId = 42"));
assertEquals(5, getCount(db, "select * from favorites where profileId = 33"));
}
@Test
public void testChangeDefaultColumn() throws Exception {
SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < 5; i++) {
ContentValues values = new ContentValues();
values.put(Favorites._ID, i);
values.put(Favorites.TITLE, "item " + i);
db.insert(Favorites.TABLE_NAME, null, values);
}
// Verify default column is 42
assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
new RestoreDbTask().changeDefaultColumn(db, 33);
// Verify default value changed
ContentValues values = new ContentValues();
values.put(Favorites._ID, 100);
values.put(Favorites.TITLE, "item 100");
db.insert(Favorites.TABLE_NAME, null, values);
assertEquals(1, getCount(db, "select * from favorites where profileId = 33"));
}
@Test
public void testSanitizeDB_bothProfiles() throws Exception {
UserHandle myUser = myUserHandle();
long myProfileId = mContext.getSystemService(UserManager.class)
.getSerialNumberForUser(myUser);
long myProfileId_old = myProfileId + 1;
long workProfileId = myProfileId + 2;
long workProfileId_old = myProfileId + 3;
MyModelDbController controller = new MyModelDbController(myProfileId);
SQLiteDatabase db = controller.getDb();
BackupManager bm = spy(new BackupManager(mContext));
doReturn(myUserHandle()).when(bm).getUserForAncestralSerialNumber(eq(myProfileId_old));
doReturn(mWorkUser).when(bm).getUserForAncestralSerialNumber(eq(workProfileId_old));
controller.users.put(workProfileId, mWorkUser);
addIconsBulk(controller, 10, 1, myProfileId_old);
addIconsBulk(controller, 6, 2, workProfileId_old);
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
mTask.sanitizeDB(mContext, controller, controller.getDb(), bm, mMockRestoreEventLogger);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
assertEquals(0, getItemCountForProfile(db, workProfileId_old));
assertEquals(10, getItemCountForProfile(db, myProfileId));
assertEquals(6, getItemCountForProfile(db, workProfileId));
}
@Test
public void testSanitizeDB_workItemsRemoved() throws Exception {
UserHandle myUser = myUserHandle();
long myProfileId = mContext.getSystemService(UserManager.class)
.getSerialNumberForUser(myUser);
long myProfileId_old = myProfileId + 1;
long workProfileId_old = myProfileId + 3;
MyModelDbController controller = new MyModelDbController(myProfileId);
SQLiteDatabase db = controller.getDb();
BackupManager bm = spy(new BackupManager(mContext));
doReturn(myUserHandle()).when(bm).getUserForAncestralSerialNumber(eq(myProfileId_old));
// Work profile is not migrated
doReturn(null).when(bm).getUserForAncestralSerialNumber(eq(workProfileId_old));
addIconsBulk(controller, 10, 1, myProfileId_old);
addIconsBulk(controller, 6, 2, workProfileId_old);
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
mTask.sanitizeDB(mContext, controller, controller.getDb(), bm, mMockRestoreEventLogger);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
assertEquals(0, getItemCountForProfile(db, workProfileId_old));
assertEquals(10, getItemCountForProfile(db, myProfileId));
assertEquals(10, getCount(db, "select * from favorites"));
}
@Test
public void givenLauncherPrefsHasNoIds_whenRestoreAppWidgetIdsIfExists_thenIdsAreRemoved() {
// When
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
}
@Test
public void givenNoPendingRestore_WhenRestoreAppWidgetIds_ThenRemoveNewWidgetIds() {
// Given
AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
int[] expectedOldIds = generateOldWidgetIds(expectedHost);
int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
when(mMockController.getDb()).thenReturn(mMockDb);
mPrefs.remove(RESTORE_DEVICE);
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
// b/343530737
verifyNoMoreInteractions(mMockController);
}
@Test
public void givenRestoreWithNonExistingWidgets_WhenRestoreAppWidgetIds_ThenRemoveNewIds() {
// Given
AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
int[] expectedOldIds = generateOldWidgetIds(expectedHost);
int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
when(mMockController.getDb()).thenReturn(mMockDb);
when(mMockDb.query(any(), any(), any(), any(), any(), any(), any())).thenReturn(
mMockCursor);
when(mMockCursor.moveToFirst()).thenReturn(false);
RestoreDbTask.setPending(mContext);
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
verify(mMockController, times(expectedOldIds.length)).update(any(), any(), any(), any());
}
@Test
public void givenRestore_WhenRestoreAppWidgetIds_ThenAddNewIds() {
// Given
AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
int[] expectedOldIds = generateOldWidgetIds(expectedHost);
int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
int[] allExpectedIds = IntStream.concat(
Arrays.stream(expectedOldIds),
Arrays.stream(expectedNewIds)
).toArray();
when(mMockController.getDb()).thenReturn(mMockDb);
when(mMockDb.query(any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockCursor);
when(mMockCursor.moveToFirst()).thenReturn(true);
when(mMockCursor.getColumnNames()).thenReturn(new String[] {});
when(mMockCursor.isAfterLast()).thenReturn(true);
RestoreDbTask.setPending(mContext);
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(allExpectedIds);
assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
verify(mMockController, times(expectedOldIds.length)).update(any(), any(), any(), any());
}
private void addIconsBulk(MyModelDbController controller,
int count, int screen, long profileId) {
int columns = LauncherAppState.getIDP(mContext).numColumns;
String packageName = getInstrumentation().getContext().getPackageName();
for (int i = 0; i < count; i++) {
ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites._ID, controller.generateNewItemId());
values.put(LauncherSettings.Favorites.CONTAINER, CONTAINER_DESKTOP);
values.put(LauncherSettings.Favorites.SCREEN, screen);
values.put(LauncherSettings.Favorites.CELLX, i % columns);
values.put(LauncherSettings.Favorites.CELLY, i / columns);
values.put(LauncherSettings.Favorites.SPANX, 1);
values.put(LauncherSettings.Favorites.SPANY, 1);
values.put(LauncherSettings.Favorites.PROFILE_ID, profileId);
values.put(LauncherSettings.Favorites.ITEM_TYPE, ITEM_TYPE_APPLICATION);
values.put(LauncherSettings.Favorites.INTENT,
new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
controller.insert(TABLE_NAME, values);
}
}
@Test
public void testRemoveScreenIdGaps_firstScreenEmpty() {
runRemoveScreenIdGapsTest(
new int[]{1, 2, 5, 6, 6, 7, 9, 9},
new int[]{1, 2, 3, 4, 4, 5, 6, 6});
}
@Test
public void testRemoveScreenIdGaps_firstScreenOccupied() {
runRemoveScreenIdGapsTest(
new int[]{0, 2, 5, 6, 6, 7, 9, 9},
new int[]{0, 1, 2, 3, 3, 4, 5, 5});
}
@Test
public void testRemoveScreenIdGaps_noGap() {
runRemoveScreenIdGapsTest(
new int[]{0, 1, 1, 2, 3, 3, 4, 5},
new int[]{0, 1, 1, 2, 3, 3, 4, 5});
}
private void runRemoveScreenIdGapsTest(int[] screenIds, int[] expectedScreenIds) {
SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < screenIds.length; i++) {
ContentValues values = new ContentValues();
values.put(Favorites._ID, i);
values.put(Favorites.SCREEN, screenIds[i]);
values.put(Favorites.CONTAINER, CONTAINER_DESKTOP);
db.insert(Favorites.TABLE_NAME, null, values);
}
// Verify items are added
assertEquals(screenIds.length,
getCount(db, "select * from favorites where container = -100"));
new RestoreDbTask().removeScreenIdGaps(db);
// verify screenId gaps removed
int[] resultScreenIds = new int[screenIds.length];
try (Cursor c = db.rawQuery(
"select screen from favorites where container = -100 order by screen", null)) {
int i = 0;
while (c.moveToNext()) {
resultScreenIds[i++] = c.getInt(0);
}
}
assertArrayEquals(expectedScreenIds, resultScreenIds);
}
public int getItemCountForProfile(SQLiteDatabase db, long profileId) {
return getCount(db, "select * from favorites where profileId = " + profileId);
}
private int getCount(SQLiteDatabase db, String sql) {
try (Cursor c = db.rawQuery(sql, null)) {
return c.getCount();
}
}
private int[] generateOldWidgetIds(AppWidgetHost host) {
// generate some widget ids in case there are none
host.allocateAppWidgetId();
host.allocateAppWidgetId();
return host.getAppWidgetIds();
}
private int[] generateNewWidgetIds(AppWidgetHost host, int[] oldWidgetIds) {
// map as many new ids as old ids
return Arrays.stream(oldWidgetIds)
.map(id -> host.allocateAppWidgetId()).toArray();
}
private class MyModelDbController extends ModelDbController {
public final LongSparseArray<UserHandle> users = new LongSparseArray<>();
MyModelDbController(long profileId) {
super(mContext);
users.put(profileId, myUserHandle());
}
@Override
public long getSerialNumberForUser(UserHandle user) {
int index = users.indexOfValue(user);
return index >= 0 ? users.keyAt(index) : -1;
}
}
private void setRestoredAppWidgetIds(Context context, int[] oldIds, int[] newIds) {
LauncherPrefs.get(context).putSync(
OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()),
APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString()));
}
}