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 junit.framework.Assert.assertEquals; 19 import static junit.framework.Assert.assertFalse; 20 import static junit.framework.Assert.assertNotSame; 21 import static junit.framework.Assert.assertTrue; 22 23 import static org.mockito.Matchers.eq; 24 import static org.mockito.Mockito.doAnswer; 25 import static org.mockito.Mockito.spy; 26 import static org.mockito.Mockito.when; 27 28 import android.content.ContentValues; 29 import android.content.Context; 30 import android.content.res.Resources; 31 import android.database.Cursor; 32 import android.database.sqlite.SQLiteDatabase; 33 import android.database.sqlite.SQLiteOpenHelper; 34 35 import com.android.launcher3.LauncherProvider; 36 import com.android.launcher3.LauncherProvider.DatabaseHelper; 37 import com.android.launcher3.LauncherSettings.Favorites; 38 import com.android.launcher3.R; 39 40 import org.junit.Before; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 import org.robolectric.RobolectricTestRunner; 44 import org.robolectric.RuntimeEnvironment; 45 46 import java.io.File; 47 48 /** 49 * Tests for {@link DbDowngradeHelper} 50 */ 51 @RunWith(RobolectricTestRunner.class) 52 public class DbDowngradeHelperTest { 53 54 private static final String SCHEMA_FILE = "test_schema.json"; 55 private static final String DB_FILE = "test.db"; 56 57 private Context mContext; 58 private File mSchemaFile; 59 private File mDbFile; 60 61 @Before setup()62 public void setup() { 63 mContext = RuntimeEnvironment.application; 64 mSchemaFile = mContext.getFileStreamPath(SCHEMA_FILE); 65 mDbFile = mContext.getDatabasePath(DB_FILE); 66 } 67 68 @Test testDowngradeSchemaMatchesVersion()69 public void testDowngradeSchemaMatchesVersion() throws Exception { 70 mSchemaFile.delete(); 71 assertFalse(mSchemaFile.exists()); 72 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 0, mContext); 73 assertEquals(LauncherProvider.SCHEMA_VERSION, DbDowngradeHelper.parse(mSchemaFile).version); 74 } 75 76 @Test testUpdateSchemaFile()77 public void testUpdateSchemaFile() throws Exception { 78 // Setup mock resources 79 Resources res = spy(mContext.getResources()); 80 doAnswer(i ->this.getClass().getResourceAsStream("/db_schema_v10.json")) 81 .when(res).openRawResource(eq(R.raw.downgrade_schema)); 82 Context context = spy(mContext); 83 when(context.getResources()).thenReturn(res); 84 85 mSchemaFile.delete(); 86 assertFalse(mSchemaFile.exists()); 87 88 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, context); 89 assertTrue(mSchemaFile.exists()); 90 assertEquals(10, DbDowngradeHelper.parse(mSchemaFile).version); 91 92 // Schema is updated on version upgrade 93 assertTrue(mSchemaFile.setLastModified(0)); 94 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 11, context); 95 assertNotSame(0, mSchemaFile.lastModified()); 96 97 // Schema is not updated when version is same 98 assertTrue(mSchemaFile.setLastModified(0)); 99 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, context); 100 assertEquals(0, mSchemaFile.lastModified()); 101 102 // Schema is not updated on version downgrade 103 DbDowngradeHelper.updateSchemaFile(mSchemaFile, 3, context); 104 assertEquals(0, mSchemaFile.lastModified()); 105 } 106 107 @Test testDowngrade_success_v24()108 public void testDowngrade_success_v24() throws Exception { 109 setupTestDb(); 110 111 TestOpenHelper helper = new TestOpenHelper(24); 112 assertEquals(24, helper.getReadableDatabase().getVersion()); 113 helper.close(); 114 } 115 116 @Test testDowngrade_success_v22()117 public void testDowngrade_success_v22() throws Exception { 118 setupTestDb(); 119 120 SQLiteOpenHelper helper = new TestOpenHelper(22); 121 assertEquals(22, helper.getWritableDatabase().getVersion()); 122 123 // Check column does not exist 124 try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME, 125 null, null, null, null, null, null)) { 126 assertEquals(-1, c.getColumnIndex(Favorites.OPTIONS)); 127 128 // Check data is present 129 assertEquals(10, c.getCount()); 130 } 131 helper.close(); 132 133 helper = new DatabaseHelper(mContext, null, DB_FILE) { 134 @Override 135 public void onOpen(SQLiteDatabase db) { } 136 }; 137 assertEquals(LauncherProvider.SCHEMA_VERSION, helper.getWritableDatabase().getVersion()); 138 139 try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME, 140 null, null, null, null, null, null)) { 141 // Check column exists 142 assertNotSame(-1, c.getColumnIndex(Favorites.OPTIONS)); 143 144 // Check data is present 145 assertEquals(10, c.getCount()); 146 } 147 helper.close(); 148 } 149 150 @Test(expected = DowngradeFailException.class) testDowngrade_fail_v20()151 public void testDowngrade_fail_v20() throws Exception { 152 setupTestDb(); 153 154 TestOpenHelper helper = new TestOpenHelper(20); 155 helper.getReadableDatabase().getVersion(); 156 } 157 setupTestDb()158 private void setupTestDb() throws Exception { 159 mSchemaFile.delete(); 160 mDbFile.delete(); 161 162 DbDowngradeHelper.updateSchemaFile(mSchemaFile, LauncherProvider.SCHEMA_VERSION, mContext); 163 164 DatabaseHelper dbHelper = new DatabaseHelper(mContext, null, DB_FILE) { 165 @Override 166 public void onOpen(SQLiteDatabase db) { } 167 }; 168 // Insert dummy data 169 for (int i = 0; i < 10; i++) { 170 ContentValues values = new ContentValues(); 171 values.put(Favorites._ID, i); 172 values.put(Favorites.TITLE, "title " + i); 173 dbHelper.getWritableDatabase().insert(Favorites.TABLE_NAME, null, values); 174 } 175 dbHelper.close(); 176 } 177 178 private class TestOpenHelper extends SQLiteOpenHelper { 179 TestOpenHelper(int version)180 public TestOpenHelper(int version) { 181 super(mContext, DB_FILE, null, version); 182 } 183 184 @Override onCreate(SQLiteDatabase sqLiteDatabase)185 public void onCreate(SQLiteDatabase sqLiteDatabase) { 186 throw new RuntimeException("DB should already be created"); 187 } 188 189 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)190 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 191 throw new RuntimeException("Only downgrade supported"); 192 } 193 194 @Override onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)195 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 196 try { 197 DbDowngradeHelper.parse(mSchemaFile).onDowngrade(db, oldVersion, newVersion); 198 } catch (Exception e) { 199 throw new DowngradeFailException(e); 200 } 201 } 202 } 203 204 private static class DowngradeFailException extends RuntimeException { DowngradeFailException(Exception e)205 public DowngradeFailException(Exception e) { 206 super(e); 207 } 208 } 209 } 210