1 /* 2 * Copyright (C) 2009 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 17 package android.database.sqlite.cts; 18 19 20 import android.content.Context; 21 import android.database.AbstractCursor; 22 import android.database.Cursor; 23 import android.database.CursorWindow; 24 import android.database.DataSetObserver; 25 import android.database.StaleDataException; 26 import android.database.sqlite.SQLiteCursor; 27 import android.database.sqlite.SQLiteDatabase; 28 import android.database.sqlite.SQLiteDirectCursorDriver; 29 import android.test.AndroidTestCase; 30 31 import java.util.Arrays; 32 33 /** 34 * Test {@link AbstractCursor}. 35 */ 36 public class SQLiteCursorTest extends AndroidTestCase { 37 private SQLiteDatabase mDatabase; 38 private static final String[] COLUMNS = new String[] { "_id", "number_1", "number_2" }; 39 private static final String TABLE_NAME = "test"; 40 private static final String TABLE_COLUMNS = " number_1 INTEGER, number_2 INTEGER"; 41 private static final int DEFAULT_TABLE_VALUE_BEGINS = 1; 42 private static final int TEST_COUNT = 10; 43 private static final String TEST_SQL = "SELECT * FROM test ORDER BY number_1"; 44 private static final String DATABASE_FILE = "database_test.db"; 45 46 @Override 47 protected void setUp() throws Exception { 48 super.setUp(); 49 getContext().deleteDatabase(DATABASE_FILE); 50 mDatabase = getContext().openOrCreateDatabase(DATABASE_FILE, Context.MODE_PRIVATE, null); 51 createTable(TABLE_NAME, TABLE_COLUMNS); 52 addValuesIntoTable(TABLE_NAME, DEFAULT_TABLE_VALUE_BEGINS, TEST_COUNT); 53 } 54 55 @Override 56 protected void tearDown() throws Exception { 57 mDatabase.close(); 58 getContext().deleteDatabase(DATABASE_FILE); 59 super.tearDown(); 60 } 61 62 public void testConstructor() { 63 SQLiteDirectCursorDriver cursorDriver = new SQLiteDirectCursorDriver(mDatabase, 64 TEST_SQL, TABLE_NAME, null); 65 try { 66 new SQLiteCursor(mDatabase, cursorDriver, TABLE_NAME, null); 67 fail("constructor didn't throw IllegalArgumentException when SQLiteQuery is null"); 68 } catch (IllegalArgumentException e) { 69 } 70 71 // get SQLiteCursor by querying database 72 SQLiteCursor cursor = getCursor(); 73 assertNotNull(cursor); 74 } 75 76 public void testClose() { 77 SQLiteCursor cursor = getCursor(); 78 assertTrue(cursor.moveToFirst()); 79 assertFalse(cursor.isClosed()); 80 assertTrue(cursor.requery()); 81 cursor.close(); 82 assertFalse(cursor.requery()); 83 try { 84 cursor.moveToFirst(); 85 fail("moveToFirst didn't throw IllegalStateException after closed."); 86 } catch (IllegalStateException e) { 87 } 88 assertTrue(cursor.isClosed()); 89 } 90 91 public void testRegisterDataSetObserver() { 92 SQLiteCursor cursor = getCursor(); 93 MockCursorWindow cursorWindow = new MockCursorWindow(false); 94 95 MockObserver observer = new MockObserver(); 96 97 cursor.setWindow(cursorWindow); 98 // Before registering, observer can't be notified. 99 assertFalse(observer.hasInvalidated()); 100 cursor.moveToLast(); 101 assertFalse(cursorWindow.isClosed()); 102 cursor.deactivate(); 103 assertFalse(observer.hasInvalidated()); 104 // deactivate() will close the CursorWindow 105 assertTrue(cursorWindow.isClosed()); 106 107 // test registering DataSetObserver 108 assertTrue(cursor.requery()); 109 cursor.registerDataSetObserver(observer); 110 assertFalse(observer.hasInvalidated()); 111 cursor.moveToLast(); 112 assertEquals(TEST_COUNT, cursor.getInt(1)); 113 cursor.deactivate(); 114 // deactivate method can invoke invalidate() method, can be observed by DataSetObserver. 115 assertTrue(observer.hasInvalidated()); 116 117 try { 118 cursor.getInt(1); 119 fail("After deactivating, cursor cannot execute getting value operations."); 120 } catch (StaleDataException e) { 121 } 122 123 assertTrue(cursor.requery()); 124 cursor.moveToLast(); 125 assertEquals(TEST_COUNT, cursor.getInt(1)); 126 127 // can't register a same observer twice. 128 try { 129 cursor.registerDataSetObserver(observer); 130 fail("didn't throw IllegalStateException when register existed observer"); 131 } catch (IllegalStateException e) { 132 } 133 134 // after unregistering, observer can't be notified. 135 cursor.unregisterDataSetObserver(observer); 136 observer.resetStatus(); 137 assertFalse(observer.hasInvalidated()); 138 cursor.deactivate(); 139 assertFalse(observer.hasInvalidated()); 140 } 141 142 public void testRequery() { 143 final String DELETE = "DELETE FROM " + TABLE_NAME + " WHERE number_1 ="; 144 final String DELETE_1 = DELETE + "1;"; 145 final String DELETE_2 = DELETE + "2;"; 146 147 mDatabase.execSQL(DELETE_1); 148 // when cursor is created, it refreshes CursorWindow and populates cursor count 149 SQLiteCursor cursor = getCursor(); 150 MockObserver observer = new MockObserver(); 151 cursor.registerDataSetObserver(observer); 152 assertEquals(TEST_COUNT - 1, cursor.getCount()); 153 assertFalse(observer.hasChanged()); 154 155 mDatabase.execSQL(DELETE_2); 156 // when getCount() has invoked once, it can no longer refresh CursorWindow. 157 assertEquals(TEST_COUNT - 1, cursor.getCount()); 158 159 assertTrue(cursor.requery()); 160 // only after requery, getCount can get most up-to-date counting info now. 161 assertEquals(TEST_COUNT - 2, cursor.getCount()); 162 assertTrue(observer.hasChanged()); 163 } 164 165 public void testRequery2() { 166 mDatabase.disableWriteAheadLogging(); 167 mDatabase.execSQL("create table testRequery2 (i int);"); 168 mDatabase.execSQL("insert into testRequery2 values(1);"); 169 mDatabase.execSQL("insert into testRequery2 values(2);"); 170 Cursor c = mDatabase.rawQuery("select * from testRequery2 order by i", null); 171 assertEquals(2, c.getCount()); 172 assertTrue(c.moveToFirst()); 173 assertEquals(1, c.getInt(0)); 174 assertTrue(c.moveToNext()); 175 assertEquals(2, c.getInt(0)); 176 // add more data to the table and requery 177 mDatabase.execSQL("insert into testRequery2 values(3);"); 178 assertTrue(c.requery()); 179 assertEquals(3, c.getCount()); 180 assertTrue(c.moveToFirst()); 181 assertEquals(1, c.getInt(0)); 182 assertTrue(c.moveToNext()); 183 assertEquals(2, c.getInt(0)); 184 assertTrue(c.moveToNext()); 185 assertEquals(3, c.getInt(0)); 186 // close the database and see if requery throws an exception 187 mDatabase.close(); 188 assertFalse(c.requery()); 189 } 190 191 public void testGetColumnIndex() { 192 SQLiteCursor cursor = getCursor(); 193 194 for (int i = 0; i < COLUMNS.length; i++) { 195 assertEquals(i, cursor.getColumnIndex(COLUMNS[i])); 196 } 197 198 assertTrue(Arrays.equals(COLUMNS, cursor.getColumnNames())); 199 } 200 201 public void testSetSelectionArguments() { 202 final String SELECTION = "_id > ?"; 203 int TEST_ARG1 = 2; 204 int TEST_ARG2 = 5; 205 SQLiteCursor cursor = (SQLiteCursor) mDatabase.query(TABLE_NAME, null, SELECTION, 206 new String[] { Integer.toString(TEST_ARG1) }, null, null, null); 207 assertEquals(TEST_COUNT - TEST_ARG1, cursor.getCount()); 208 cursor.setSelectionArguments(new String[] { Integer.toString(TEST_ARG2) }); 209 cursor.requery(); 210 assertEquals(TEST_COUNT - TEST_ARG2, cursor.getCount()); 211 } 212 213 public void testOnMove() { 214 // Do not test this API. It is callback which: 215 // 1. The callback mechanism has been tested in super class 216 // 2. The functionality is implementation details, no need to test 217 } 218 219 private void createTable(String tableName, String columnNames) { 220 String sql = "Create TABLE " + tableName + " (_id INTEGER PRIMARY KEY, " 221 + columnNames + " );"; 222 mDatabase.execSQL(sql); 223 } 224 225 private void addValuesIntoTable(String tableName, int start, int end) { 226 for (int i = start; i <= end; i++) { 227 mDatabase.execSQL("INSERT INTO " + tableName + "(number_1) VALUES ('" + i + "');"); 228 } 229 } 230 231 private SQLiteCursor getCursor() { 232 SQLiteCursor cursor = (SQLiteCursor) mDatabase.query(TABLE_NAME, null, null, 233 null, null, null, null); 234 return cursor; 235 } 236 237 private class MockObserver extends DataSetObserver { 238 private boolean mHasChanged = false; 239 private boolean mHasInvalidated = false; 240 241 @Override 242 public void onChanged() { 243 super.onChanged(); 244 mHasChanged = true; 245 } 246 247 @Override 248 public void onInvalidated() { 249 super.onInvalidated(); 250 mHasInvalidated = true; 251 } 252 253 protected void resetStatus() { 254 mHasChanged = false; 255 mHasInvalidated = false; 256 } 257 258 protected boolean hasChanged() { 259 return mHasChanged; 260 } 261 262 protected boolean hasInvalidated () { 263 return mHasInvalidated; 264 } 265 } 266 267 private class MockCursorWindow extends CursorWindow { 268 private boolean mIsClosed = false; 269 270 public MockCursorWindow(boolean localWindow) { 271 super(localWindow); 272 } 273 274 @Override 275 public void close() { 276 super.close(); 277 mIsClosed = true; 278 } 279 280 public boolean isClosed() { 281 return mIsClosed; 282 } 283 } 284 } 285