1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.model; 17 18 import static androidx.test.InstrumentationRegistry.getContext; 19 20 import static junit.framework.Assert.assertEquals; 21 import static junit.framework.Assert.assertFalse; 22 import static junit.framework.Assert.assertNotSame; 23 import static junit.framework.Assert.assertTrue; 24 25 import static org.mockito.Mockito.doAnswer; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.when; 28 29 import android.content.ContentValues; 30 import android.content.Context; 31 import android.content.res.Resources; 32 import android.database.Cursor; 33 import android.database.sqlite.SQLiteDatabase; 34 import android.database.sqlite.SQLiteOpenHelper; 35 36 import androidx.test.ext.junit.runners.AndroidJUnit4; 37 import androidx.test.filters.SmallTest; 38 import androidx.test.platform.app.InstrumentationRegistry; 39 40 import com.android.launcher3.LauncherSettings.Favorites; 41 import com.android.launcher3.R; 42 import com.android.launcher3.pm.UserCache; 43 44 import org.junit.Before; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 48 import java.io.File; 49 50 /** 51 * Tests for {@link DbDowngradeHelper} 52 */ 53 @SmallTest 54 @RunWith(AndroidJUnit4.class) 55 public class DbDowngradeHelperTest { 56 57 private static final String SCHEMA_FILE = "test_schema.json"; 58 private static final String DB_FILE = "test.db"; 59 60 private Context mContext; 61 private File mSchemaFile; 62 private File mDbFile; 63 64 @Before setup()65 public void setup() { 66 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 67 mSchemaFile = mContext.getFileStreamPath(SCHEMA_FILE); 68 mDbFile = mContext.getDatabasePath(DB_FILE); 69 } 70 71 @Test testDowngradeSchemaMatchesVersion()72 public void testDowngradeSchemaMatchesVersion() throws Exception { 73 mSchemaFile.delete(); 74 assertFalse(mSchemaFile.exists()); 75 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 0, mContext); 76 assertEquals(DatabaseHelper.SCHEMA_VERSION, DbDowngradeHelper.parse(mSchemaFile).version); 77 } 78 79 @Test testUpdateSchemaFile()80 public void testUpdateSchemaFile() throws Exception { 81 // Setup mock resources 82 Resources res = spy(mContext.getResources()); 83 Resources myRes = getContext().getResources(); 84 doAnswer(i -> myRes.openRawResource( 85 myRes.getIdentifier("db_schema_v10", "raw", getContext().getPackageName()))) 86 .when(res).openRawResource(R.raw.downgrade_schema); 87 Context context = spy(mContext); 88 when(context.getResources()).thenReturn(res); 89 90 mSchemaFile.delete(); 91 assertFalse(mSchemaFile.exists()); 92 93 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, context); 94 assertTrue(mSchemaFile.exists()); 95 assertEquals(10, DbDowngradeHelper.parse(mSchemaFile).version); 96 97 // Schema is updated on version upgrade 98 assertTrue(mSchemaFile.setLastModified(0)); 99 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 11, context); 100 assertNotSame(0, mSchemaFile.lastModified()); 101 102 // Schema is not updated when version is same 103 assertTrue(mSchemaFile.setLastModified(0)); 104 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, context); 105 assertEquals(0, mSchemaFile.lastModified()); 106 107 // Schema is not updated on version downgrade 108 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 3, context); 109 assertEquals(0, mSchemaFile.lastModified()); 110 } 111 112 @Test testDowngrade_success_v31()113 public void testDowngrade_success_v31() throws Exception { 114 setupTestDb(); 115 116 try (SQLiteOpenHelper helper = new MyDatabaseHelper()) { 117 assertFalse(hasFavoritesColumn(helper.getWritableDatabase(), "iconPackage")); 118 assertFalse(hasFavoritesColumn(helper.getWritableDatabase(), "iconResource")); 119 } 120 121 try (TestOpenHelper helper = new TestOpenHelper(24)) { 122 assertTrue(hasFavoritesColumn(helper.getWritableDatabase(), "iconPackage")); 123 assertTrue(hasFavoritesColumn(helper.getWritableDatabase(), "iconResource")); 124 } 125 } 126 127 @Test testDowngrade_success_v24()128 public void testDowngrade_success_v24() throws Exception { 129 setupTestDb(); 130 131 TestOpenHelper helper = new TestOpenHelper(24); 132 assertEquals(24, helper.getReadableDatabase().getVersion()); 133 helper.close(); 134 } 135 136 @Test testDowngrade_success_v22()137 public void testDowngrade_success_v22() throws Exception { 138 setupTestDb(); 139 140 try (SQLiteOpenHelper helper = new TestOpenHelper(22)) { 141 assertEquals(22, helper.getWritableDatabase().getVersion()); 142 assertFalse(hasFavoritesColumn(helper.getWritableDatabase(), Favorites.OPTIONS)); 143 assertEquals(10, getFavoriteDataCount(helper.getWritableDatabase())); 144 } 145 146 try (SQLiteOpenHelper helper = new MyDatabaseHelper()) { 147 assertEquals(DatabaseHelper.SCHEMA_VERSION, 148 helper.getWritableDatabase().getVersion()); 149 assertTrue(hasFavoritesColumn(helper.getWritableDatabase(), Favorites.OPTIONS)); 150 assertEquals(10, getFavoriteDataCount(helper.getWritableDatabase())); 151 } 152 } 153 154 @Test(expected = DowngradeFailException.class) testDowngrade_fail_v20()155 public void testDowngrade_fail_v20() throws Exception { 156 setupTestDb(); 157 158 TestOpenHelper helper = new TestOpenHelper(20); 159 helper.getReadableDatabase().getVersion(); 160 } 161 setupTestDb()162 private void setupTestDb() throws Exception { 163 mSchemaFile.delete(); 164 mDbFile.delete(); 165 166 DbDowngradeHelper.updateSchemaFile(mSchemaFile, DatabaseHelper.SCHEMA_VERSION, mContext); 167 168 DatabaseHelper dbHelper = new MyDatabaseHelper(); 169 // Insert mock data 170 for (int i = 0; i < 10; i++) { 171 ContentValues values = new ContentValues(); 172 values.put(Favorites._ID, i); 173 values.put(Favorites.TITLE, "title " + i); 174 dbHelper.getWritableDatabase().insert(Favorites.TABLE_NAME, null, values); 175 } 176 dbHelper.close(); 177 } 178 179 private class TestOpenHelper extends SQLiteOpenHelper { 180 TestOpenHelper(int version)181 public TestOpenHelper(int version) { 182 super(mContext, DB_FILE, null, version); 183 } 184 185 @Override onCreate(SQLiteDatabase sqLiteDatabase)186 public void onCreate(SQLiteDatabase sqLiteDatabase) { 187 throw new RuntimeException("DB should already be created"); 188 } 189 190 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)191 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 192 throw new RuntimeException("Only downgrade supported"); 193 } 194 195 @Override onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)196 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 197 try { 198 DbDowngradeHelper.parse(mSchemaFile).onDowngrade(db, oldVersion, newVersion); 199 } catch (Exception e) { 200 throw new DowngradeFailException(e); 201 } 202 } 203 } 204 205 private static class DowngradeFailException extends RuntimeException { DowngradeFailException(Exception e)206 public DowngradeFailException(Exception e) { 207 super(e); 208 } 209 } 210 hasFavoritesColumn(SQLiteDatabase db, String columnName)211 private static boolean hasFavoritesColumn(SQLiteDatabase db, String columnName) { 212 try (Cursor c = db.query(Favorites.TABLE_NAME, null, null, null, null, null, null)) { 213 return c.getColumnIndex(columnName) >= 0; 214 } 215 } 216 getFavoriteDataCount(SQLiteDatabase db)217 public static int getFavoriteDataCount(SQLiteDatabase db) { 218 try (Cursor c = db.query(Favorites.TABLE_NAME, null, null, null, null, null, null)) { 219 return c.getCount(); 220 } 221 } 222 223 private class MyDatabaseHelper extends DatabaseHelper { 224 MyDatabaseHelper()225 MyDatabaseHelper() { 226 super(mContext, DB_FILE, 227 UserCache.INSTANCE.get(mContext)::getSerialNumberForUser, 228 () -> { }); 229 } 230 231 @Override onOpen(SQLiteDatabase db)232 public void onOpen(SQLiteDatabase db) { } 233 } 234 } 235