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