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 abstract 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 172 final String filename = getDatabaseFilename(); 173 if (filename == null) { 174 mDb = SQLiteDatabase.create(null); 175 } else { 176 getContext().deleteDatabase(filename); 177 mDb = SQLiteDatabase.openOrCreateDatabase(filename, null); 178 } 179 } 180 181 @Override tearDown()182 protected void tearDown() throws Exception { 183 mDb.close(); 184 super.tearDown(); 185 } 186 getDatabaseFilename()187 protected abstract String getDatabaseFilename(); 188 assertDatabaseStructureSameAsList(TableListEntry[] list, boolean isNewDatabase)189 protected void assertDatabaseStructureSameAsList(TableListEntry[] list, boolean isNewDatabase) { 190 for (TableListEntry entry : list) { 191 if (!entry.shouldBeInNewDb) { 192 if (isNewDatabase) { 193 continue; 194 } 195 } 196 TableStructure structure = new TableStructure(mDb, entry.name); 197 structure.assertSame(entry.columns); 198 } 199 } 200 testAssertHasColumn_Match()201 public void testAssertHasColumn_Match() { 202 TableStructure table = createOneColumnTable("foo", INTEGER, false, null); 203 table.assertHasColumn("foo", INTEGER, false, null); 204 } 205 testAssertHasColumn_Empty()206 public void testAssertHasColumn_Empty() { 207 TableStructure table = new TableStructure(); 208 209 try { 210 table.assertHasColumn("bar", INTEGER, false, null); 211 throw new AssertionError("Assert should fail"); 212 } catch (AssertionFailedError e) { 213 // Should fail 214 } 215 } 216 testAssertHasColumn_ColumnNotExist()217 public void testAssertHasColumn_ColumnNotExist() { 218 TableStructure table = createOneColumnTable("foo", INTEGER, false, null); 219 220 try { 221 table.assertHasColumn("bar", INTEGER, false, null); 222 throw new AssertionError("Assert should fail"); 223 } catch (AssertionFailedError e) { 224 // Should fail 225 } 226 } 227 testAssertHasColumn_TypeMismatch()228 public void testAssertHasColumn_TypeMismatch() { 229 TableStructure table = createOneColumnTable("foo", INTEGER, false, null); 230 231 try { 232 table.assertHasColumn("foo", TEXT, false, null); 233 throw new AssertionError("Assert should fail"); 234 } catch (AssertionFailedError e) { 235 // Should fail 236 } 237 } 238 testAssertHasColumn_NotNullMismatch()239 public void testAssertHasColumn_NotNullMismatch() { 240 TableStructure table = createOneColumnTable("foo", INTEGER, false, null); 241 242 try { 243 table.assertHasColumn("foo", INTEGER, true, null); 244 throw new AssertionError("Assert should fail"); 245 } catch (AssertionFailedError e) { 246 // Should fail 247 } 248 } 249 testAssertHasColumn_DefaultMatch()250 public void testAssertHasColumn_DefaultMatch() { 251 TableStructure table = createOneColumnTable("foo", INTEGER, false, "baz"); 252 table.assertHasColumn("foo", INTEGER, false, "baz"); 253 } 254 testAssertHasColumn_DefaultMismatch()255 public void testAssertHasColumn_DefaultMismatch() { 256 TableStructure table = createOneColumnTable("foo", INTEGER, false, "bar"); 257 258 try { 259 table.assertHasColumn("foo", INTEGER, false, "baz"); 260 throw new AssertionError("Assert should fail"); 261 } catch (AssertionFailedError e) { 262 // Should fail 263 } 264 } 265 testAssertHasColumn_DefaultMismatch_Null1()266 public void testAssertHasColumn_DefaultMismatch_Null1() { 267 TableStructure table = createOneColumnTable("foo", INTEGER, false, null); 268 269 try { 270 table.assertHasColumn("foo", INTEGER, false, "baz"); 271 throw new AssertionError("Assert should fail"); 272 } catch (AssertionFailedError e) { 273 // Should fail 274 } 275 } 276 testAssertHasColumn_DefaultMismatch_Null2()277 public void testAssertHasColumn_DefaultMismatch_Null2() { 278 TableStructure table = createOneColumnTable("foo", INTEGER, false, "baz"); 279 280 try { 281 table.assertHasColumn("foo", INTEGER, false, null); 282 throw new AssertionError("Assert should fail"); 283 } catch (AssertionFailedError e) { 284 // Should fail 285 } 286 } 287 createOneColumnTable(String name, String type, boolean notnull, String defaultValue)288 private TableStructure createOneColumnTable(String name, String type, boolean notnull, 289 String defaultValue) { 290 TableStructure table = new TableStructure(); 291 table.addColumn(new TableColumn(name, type, notnull, defaultValue)); 292 return table; 293 } 294 295 } 296