1 /*
2  * Copyright (C) 2015 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.providers.contacts;
17 
18 import android.database.Cursor;
19 import android.database.sqlite.SQLiteDatabase;
20 import android.test.AndroidTestCase;
21 
22 import junit.framework.AssertionFailedError;
23 
24 import java.util.HashMap;
25 
26 /**
27  * Unit tests for database create/upgrade operations.
28  *
29  * Run the test like this: <code> runtest -c com.android.providers.contacts.BaseDatabaseHelperUpgradeTest
30  * contactsprov </code>
31  */
32 public class BaseDatabaseHelperUpgradeTest extends AndroidTestCase {
33 
34     protected static final String INTEGER = "INTEGER";
35     protected static final String TEXT = "TEXT";
36     protected static final String STRING = "STRING";
37     protected static final String BLOB = "BLOB";
38 
39     protected SQLiteDatabase mDb;
40 
41     /**
42      * The column info returned by PRAGMA table_info()
43      */
44     protected static class TableColumn {
45 
46         public int cid;
47         public String name;
48         public String type;
49         public boolean notnull;
50         // default value
51         public String dflt_value;
52         // primary key. Not tested.
53         public int pk;
54 
TableColumn()55         public TableColumn() {
56 
57         }
58 
TableColumn(String name, String type, boolean notnull, String defaultValue)59         public TableColumn(String name, String type, boolean notnull, String defaultValue) {
60             this.name = name;
61             this.type = type;
62             this.notnull = notnull;
63             this.dflt_value = defaultValue;
64         }
65     }
66 
67     protected static class TableStructure {
68 
69         private final HashMap<String, TableColumn> mColumns = new HashMap<String, TableColumn>();
70         private final String mName;
71 
TableStructure(SQLiteDatabase db, String tableName)72         public TableStructure(SQLiteDatabase db, String tableName) {
73             mName = tableName;
74             try (final Cursor cursor = db.rawQuery("PRAGMA table_info(" + tableName + ");", null)) {
75                 final int cidIndex = cursor.getColumnIndex("cid");
76                 final int nameIndex = cursor.getColumnIndex("name");
77                 final int typeIndex = cursor.getColumnIndex("type");
78                 final int notNullIndex = cursor.getColumnIndex("notnull");
79                 final int dfltValueIndex = cursor.getColumnIndex("dflt_value");
80                 final int pkIndex = cursor.getColumnIndex("pk");
81                 cursor.moveToPosition(-1);
82                 while (cursor.moveToNext()) {
83                     TableColumn column = new TableColumn();
84                     column.cid = cursor.getInt(cidIndex);
85                     column.name = cursor.getString(nameIndex);
86                     column.type = cursor.getString(typeIndex);
87                     column.notnull = cursor.getInt(notNullIndex) != 0;
88                     column.dflt_value = cursor.getString(dfltValueIndex);
89                     column.pk = cursor.getInt(pkIndex);
90 
91                     addColumn(column);
92                 }
93             }
94         }
95 
TableStructure()96         private TableStructure() {
97             mName = "";
98         }
99 
addColumn(TableColumn column)100         private void addColumn(TableColumn column) {
101             mColumns.put(column.name, column);
102         }
103 
assertHasColumn(String name, String type, boolean notnull, String defaultValue)104         public void assertHasColumn(String name, String type, boolean notnull,
105                 String defaultValue) {
106             final TableColumn column = mColumns.get(name);
107             if (column == null) {
108                 throw new AssertionFailedError("Table " + mName + ": Column missing: " + name);
109             }
110             if (!type.equals(column.type)) {
111                 throw new AssertionFailedError("Table " + mName + ": Column " + name + " type:"
112                         + column.type + ", " + type + " expected");
113             }
114             if (!notnull == column.notnull) {
115                 throw new AssertionFailedError("Table " + mName + ": Column " + name + " notnull:"
116                         + column.notnull + ", " + notnull + " expected");
117             }
118             if (defaultValue == null) {
119                 if (column.dflt_value != null) {
120                     throw new AssertionFailedError("Table " + mName + ": Column " + name
121                             + " defaultValue: " + column.dflt_value + ", null expected");
122                 }
123             } else if (!defaultValue.equals(column.dflt_value)) {
124                 throw new AssertionFailedError("Table " + mName + ": Column " + name
125                         + " defaultValue:" + column.dflt_value + ", " + defaultValue + " expected");
126             }
127         }
128 
129 
assertHasColumns(TableColumn[] columns)130         public void assertHasColumns(TableColumn[] columns) {
131             for (final TableColumn column : columns) {
132                 assertHasColumn(column.name, column.type, column.notnull, column.dflt_value);
133             }
134         }
135 
136         /**
137          * Assert the TableStructure has every column in @param columns, and nothing else.
138          */
assertSame(TableColumn[] columns)139         public void assertSame(TableColumn[] columns) {
140             assertHasColumns(columns);
141             if (columns.length != mColumns.size()) {
142                 throw new RuntimeException("column count mismatch");
143             }
144         }
145 
146     }
147 
148     /**
149      * Used to store a tables' name and its' current structure in a array.
150      */
151     protected static class TableListEntry {
152 
153         public final String name;
154         public final TableColumn[] columns;
155         public final boolean shouldBeInNewDb;
156 
TableListEntry(String name, TableColumn[] columns)157         public TableListEntry(String name, TableColumn[] columns) {
158             this(name, columns, /* shouldBeInNewDb = */ true);
159         }
160 
TableListEntry(String name, TableColumn[] columns, boolean shouldBeInNewDb)161         public TableListEntry(String name, TableColumn[] columns, boolean shouldBeInNewDb) {
162             this.name = name;
163             this.columns = columns;
164             this.shouldBeInNewDb = shouldBeInNewDb;
165         }
166     }
167 
168     @Override
setUp()169     protected void setUp() throws Exception {
170         super.setUp();
171         mDb = SQLiteDatabase.create(null);
172     }
173 
174     @Override
tearDown()175     protected void tearDown() throws Exception {
176         mDb.close();
177         super.tearDown();
178     }
179 
assertDatabaseStructureSameAsList(TableListEntry[] list, boolean isNewDatabase)180     protected void assertDatabaseStructureSameAsList(TableListEntry[] list, boolean isNewDatabase) {
181         for (TableListEntry entry : list) {
182             if (!entry.shouldBeInNewDb) {
183                 if (isNewDatabase) {
184                     continue;
185                 }
186             }
187             TableStructure structure = new TableStructure(mDb, entry.name);
188             structure.assertSame(entry.columns);
189         }
190     }
191 
testAssertHasColumn_Match()192     public void testAssertHasColumn_Match() {
193         TableStructure table = createOneColumnTable("foo", INTEGER, false, null);
194         table.assertHasColumn("foo", INTEGER, false, null);
195     }
196 
testAssertHasColumn_Empty()197     public void testAssertHasColumn_Empty() {
198         TableStructure table = new TableStructure();
199 
200         try {
201             table.assertHasColumn("bar", INTEGER, false, null);
202             throw new AssertionError("Assert should fail");
203         } catch (AssertionFailedError e) {
204             // Should fail
205         }
206     }
207 
testAssertHasColumn_ColumnNotExist()208     public void testAssertHasColumn_ColumnNotExist() {
209         TableStructure table = createOneColumnTable("foo", INTEGER, false, null);
210 
211         try {
212             table.assertHasColumn("bar", INTEGER, false, null);
213             throw new AssertionError("Assert should fail");
214         } catch (AssertionFailedError e) {
215             // Should fail
216         }
217     }
218 
testAssertHasColumn_TypeMismatch()219     public void testAssertHasColumn_TypeMismatch() {
220         TableStructure table = createOneColumnTable("foo", INTEGER, false, null);
221 
222         try {
223             table.assertHasColumn("foo", TEXT, false, null);
224             throw new AssertionError("Assert should fail");
225         } catch (AssertionFailedError e) {
226             // Should fail
227         }
228     }
229 
testAssertHasColumn_NotNullMismatch()230     public void testAssertHasColumn_NotNullMismatch() {
231         TableStructure table = createOneColumnTable("foo", INTEGER, false, null);
232 
233         try {
234             table.assertHasColumn("foo", INTEGER, true, null);
235             throw new AssertionError("Assert should fail");
236         } catch (AssertionFailedError e) {
237             // Should fail
238         }
239     }
240 
testAssertHasColumn_DefaultMatch()241     public void testAssertHasColumn_DefaultMatch() {
242         TableStructure table = createOneColumnTable("foo", INTEGER, false, "baz");
243         table.assertHasColumn("foo", INTEGER, false, "baz");
244     }
245 
testAssertHasColumn_DefaultMismatch()246     public void testAssertHasColumn_DefaultMismatch() {
247         TableStructure table = createOneColumnTable("foo", INTEGER, false, "bar");
248 
249         try {
250             table.assertHasColumn("foo", INTEGER, false, "baz");
251             throw new AssertionError("Assert should fail");
252         } catch (AssertionFailedError e) {
253             // Should fail
254         }
255     }
256 
testAssertHasColumn_DefaultMismatch_Null1()257     public void testAssertHasColumn_DefaultMismatch_Null1() {
258         TableStructure table = createOneColumnTable("foo", INTEGER, false, null);
259 
260         try {
261             table.assertHasColumn("foo", INTEGER, false, "baz");
262             throw new AssertionError("Assert should fail");
263         } catch (AssertionFailedError e) {
264             // Should fail
265         }
266     }
267 
testAssertHasColumn_DefaultMismatch_Null2()268     public void testAssertHasColumn_DefaultMismatch_Null2() {
269         TableStructure table = createOneColumnTable("foo", INTEGER, false, "baz");
270 
271         try {
272             table.assertHasColumn("foo", INTEGER, false, null);
273             throw new AssertionError("Assert should fail");
274         } catch (AssertionFailedError e) {
275             // Should fail
276         }
277     }
278 
createOneColumnTable(String name, String type, boolean notnull, String defaultValue)279     private TableStructure createOneColumnTable(String name, String type, boolean notnull,
280             String defaultValue) {
281         TableStructure table = new TableStructure();
282         table.addColumn(new TableColumn(name, type, notnull, defaultValue));
283         return table;
284     }
285 
286 }
287