1 /* 2 * Copyright (C) 2007 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.cts; 18 19 import android.content.ContentValues; 20 import android.content.Context; 21 import android.database.Cursor; 22 import android.database.CursorIndexOutOfBoundsException; 23 import android.database.CursorWrapper; 24 import android.database.DataSetObserver; 25 import android.database.DatabaseUtils; 26 import android.database.sqlite.SQLiteCursor; 27 import android.database.sqlite.SQLiteCursorDriver; 28 import android.database.sqlite.SQLiteDatabase; 29 import android.database.sqlite.SQLiteQuery; 30 import android.database.sqlite.SQLiteStatement; 31 import android.os.Looper; 32 import android.test.AndroidTestCase; 33 import android.test.PerformanceTestCase; 34 import android.test.suitebuilder.annotation.LargeTest; 35 import android.test.suitebuilder.annotation.MediumTest; 36 import android.util.Log; 37 38 import java.io.File; 39 import java.util.Arrays; 40 import java.util.Random; 41 42 public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTestCase { 43 private static final String sString1 = "this is a test"; 44 private static final String sString2 = "and yet another test"; 45 private static final String sString3 = "this string is a little longer, but still a test"; 46 47 private static final int CURRENT_DATABASE_VERSION = 42; 48 private SQLiteDatabase mDatabase; 49 private File mDatabaseFile; 50 protected static final int TYPE_CURSOR = 0; 51 protected static final int TYPE_CURSORWRAPPER = 1; 52 private int mTestType = TYPE_CURSOR; 53 54 @Override 55 protected void setUp() throws Exception { 56 super.setUp(); 57 File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE); 58 mDatabaseFile = new File(dbDir, "database_test.db"); 59 if (mDatabaseFile.exists()) { 60 mDatabaseFile.delete(); 61 } 62 mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null); 63 assertNotNull(mDatabase); 64 mDatabase.setVersion(CURRENT_DATABASE_VERSION); 65 } 66 67 @Override 68 protected void tearDown() throws Exception { 69 mDatabase.close(); 70 mDatabaseFile.delete(); 71 super.tearDown(); 72 } 73 74 public void setupTestType(int testType) { 75 mTestType = testType; 76 } 77 78 private Cursor getTestCursor(Cursor cursor) { 79 switch (mTestType) { 80 case TYPE_CURSORWRAPPER: 81 return new CursorWrapper(cursor); 82 case TYPE_CURSOR: 83 default: 84 return cursor; 85 } 86 } 87 88 public boolean isPerformanceOnly() { 89 return false; 90 } 91 92 // These test can only be run once. 93 public int startPerformance(Intermediates intermediates) { 94 return 1; 95 } 96 97 private void populateDefaultTable() { 98 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);"); 99 100 mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');"); 101 mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');"); 102 mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');"); 103 } 104 105 @MediumTest 106 public void testBlob() throws Exception { 107 // create table 108 mDatabase.execSQL( 109 "CREATE TABLE test (_id INTEGER PRIMARY KEY, s TEXT, d REAL, l INTEGER, b BLOB);"); 110 // insert blob 111 Object[] args = new Object[4]; 112 113 byte[] blob = new byte[1000]; 114 byte value = 99; 115 Arrays.fill(blob, value); 116 args[3] = blob; 117 118 String s = new String("text"); 119 args[0] = s; 120 Double d = 99.9; 121 args[1] = d; 122 Long l = (long) 1000; 123 args[2] = l; 124 125 String sql = "INSERT INTO test (s, d, l, b) VALUES (?,?,?,?)"; 126 mDatabase.execSQL(sql, args); 127 // use cursor to access blob 128 129 Cursor testCursor = mDatabase.query("test", null, null, null, null, null, null); 130 131 testCursor.moveToNext(); 132 ContentValues cv = new ContentValues(); 133 DatabaseUtils.cursorRowToContentValues(testCursor, cv); 134 135 int bCol = testCursor.getColumnIndexOrThrow("b"); 136 int sCol = testCursor.getColumnIndexOrThrow("s"); 137 int dCol = testCursor.getColumnIndexOrThrow("d"); 138 int lCol = testCursor.getColumnIndexOrThrow("l"); 139 byte[] cBlob = testCursor.getBlob(bCol); 140 assertTrue(Arrays.equals(blob, cBlob)); 141 assertEquals(s, testCursor.getString(sCol)); 142 assertEquals((double) d, testCursor.getDouble(dCol)); 143 assertEquals((long) l, testCursor.getLong(lCol)); 144 } 145 146 @MediumTest 147 public void testRealColumns() throws Exception { 148 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data REAL);"); 149 ContentValues values = new ContentValues(); 150 values.put("data", 42.11); 151 long id = mDatabase.insert("test", "data", values); 152 assertTrue(id > 0); 153 Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT data FROM test", null)); 154 assertNotNull(testCursor); 155 assertTrue(testCursor.moveToFirst()); 156 assertEquals(42.11, testCursor.getDouble(0)); 157 testCursor.close(); 158 } 159 160 @MediumTest 161 public void testCursor1() throws Exception { 162 populateDefaultTable(); 163 164 Cursor testCursor = getTestCursor(mDatabase.query("test", null, null, null, null, null, 165 null)); 166 167 int dataColumn = testCursor.getColumnIndexOrThrow("data"); 168 169 // The cursor should ignore text before the last period when looking for a column. (This 170 // is a temporary hack in all implementations of getColumnIndex.) 171 int dataColumn2 = testCursor.getColumnIndexOrThrow("junk.data"); 172 assertEquals(dataColumn, dataColumn2); 173 174 assertSame(3, testCursor.getCount()); 175 176 assertTrue(testCursor.isBeforeFirst()); 177 178 try { 179 testCursor.getInt(0); 180 fail("CursorIndexOutOfBoundsException expected"); 181 } catch (CursorIndexOutOfBoundsException ex) { 182 // expected 183 } 184 185 testCursor.moveToNext(); 186 assertEquals(1, testCursor.getInt(0)); 187 188 String s = testCursor.getString(dataColumn); 189 assertEquals(sString1, s); 190 191 testCursor.moveToNext(); 192 s = testCursor.getString(dataColumn); 193 assertEquals(sString2, s); 194 195 testCursor.moveToNext(); 196 s = testCursor.getString(dataColumn); 197 assertEquals(sString3, s); 198 199 testCursor.moveToPosition(-1); 200 testCursor.moveToNext(); 201 s = testCursor.getString(dataColumn); 202 assertEquals(sString1, s); 203 204 testCursor.moveToPosition(2); 205 s = testCursor.getString(dataColumn); 206 assertEquals(sString3, s); 207 208 int i; 209 210 for (testCursor.moveToFirst(), i = 0; !testCursor.isAfterLast(); 211 testCursor.moveToNext(), i++) { 212 testCursor.getInt(0); 213 } 214 215 assertEquals(3, i); 216 217 try { 218 testCursor.getInt(0); 219 fail("CursorIndexOutOfBoundsException expected"); 220 } catch (CursorIndexOutOfBoundsException ex) { 221 // expected 222 } 223 testCursor.close(); 224 } 225 226 @MediumTest 227 public void testCursor2() throws Exception { 228 populateDefaultTable(); 229 230 Cursor testCursor = getTestCursor(mDatabase.query("test", null, "_id > 1000", null, null, 231 null, null)); 232 assertEquals(0, testCursor.getCount()); 233 assertTrue(testCursor.isBeforeFirst()); 234 235 try { 236 testCursor.getInt(0); 237 fail("CursorIndexOutOfBoundsException expected"); 238 } catch (CursorIndexOutOfBoundsException ex) { 239 // expected 240 } 241 242 int i; 243 for (testCursor.moveToFirst(), i = 0; !testCursor.isAfterLast(); 244 testCursor.moveToNext(), i++) { 245 testCursor.getInt(0); 246 } 247 assertEquals(0, i); 248 try { 249 testCursor.getInt(0); 250 fail("CursorIndexOutOfBoundsException expected"); 251 } catch (CursorIndexOutOfBoundsException ex) { 252 // expected 253 } 254 testCursor.close(); 255 } 256 257 @MediumTest 258 public void testLargeField() throws Exception { 259 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);"); 260 261 StringBuilder sql = new StringBuilder(2100); 262 sql.append("INSERT INTO test (data) VALUES ('"); 263 Random random = new Random(System.currentTimeMillis()); 264 StringBuilder randomString = new StringBuilder(1979); 265 for (int i = 0; i < 1979; i++) { 266 randomString.append((random.nextInt() & 0xf) % 10); 267 } 268 sql.append(randomString); 269 sql.append("');"); 270 mDatabase.execSQL(sql.toString()); 271 272 Cursor testCursor = getTestCursor(mDatabase.query("test", null, null, null, null, null, 273 null)); 274 assertNotNull(testCursor); 275 assertEquals(1, testCursor.getCount()); 276 277 assertTrue(testCursor.moveToFirst()); 278 assertEquals(0, testCursor.getPosition()); 279 String largeString = testCursor.getString(testCursor.getColumnIndexOrThrow("data")); 280 assertNotNull(largeString); 281 assertEquals(randomString.toString(), largeString); 282 testCursor.close(); 283 } 284 285 private class TestObserver extends DataSetObserver { 286 int total; 287 SQLiteCursor c; 288 boolean quit = false; 289 290 public TestObserver(int total_, SQLiteCursor cursor) { 291 c = cursor; 292 total = total_; 293 } 294 295 @Override 296 public void onChanged() { 297 int count = c.getCount(); 298 if (total == count) { 299 int i = 0; 300 while (c.moveToNext()) { 301 assertEquals(i, c.getInt(1)); 302 i++; 303 } 304 assertEquals(count, i); 305 quit = true; 306 Looper.myLooper().quit(); 307 } 308 } 309 310 @Override 311 public void onInvalidated() { 312 } 313 } 314 315 @LargeTest 316 public void testManyRowsLong() throws Exception { 317 mDatabase.beginTransaction(); 318 final int count = 9000; 319 try { 320 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); 321 322 for (int i = 0; i < count; i++) { 323 mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");"); 324 } 325 mDatabase.setTransactionSuccessful(); 326 } finally { 327 mDatabase.endTransaction(); 328 } 329 330 Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "data" }, 331 null, null, null, null, null)); 332 assertNotNull(testCursor); 333 334 int i = 0; 335 while (testCursor.moveToNext()) { 336 assertEquals(i, testCursor.getInt(0)); 337 i++; 338 } 339 assertEquals(count, i); 340 assertEquals(count, testCursor.getCount()); 341 342 Log.d("testManyRows", "count " + Integer.toString(i)); 343 testCursor.close(); 344 } 345 346 @LargeTest 347 public void testManyRowsTxt() throws Exception { 348 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);"); 349 StringBuilder sql = new StringBuilder(2100); 350 sql.append("INSERT INTO test (data) VALUES ('"); 351 Random random = new Random(System.currentTimeMillis()); 352 StringBuilder randomString = new StringBuilder(1979); 353 for (int i = 0; i < 1979; i++) { 354 randomString.append((random.nextInt() & 0xf) % 10); 355 } 356 sql.append(randomString); 357 sql.append("');"); 358 359 // if cursor window size changed, adjust this value too 360 final int count = 600; // more than two fillWindow needed 361 for (int i = 0; i < count; i++) { 362 mDatabase.execSQL(sql.toString()); 363 } 364 365 Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "data" }, null, 366 null, null, null, null)); 367 assertNotNull(testCursor); 368 369 int i = 0; 370 while (testCursor.moveToNext()) { 371 assertEquals(randomString.toString(), testCursor.getString(0)); 372 i++; 373 } 374 assertEquals(count, i); 375 assertEquals(count, testCursor.getCount()); 376 testCursor.close(); 377 } 378 379 @LargeTest 380 public void testManyRowsTxtLong() throws Exception { 381 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, txt TEXT, data INT);"); 382 383 Random random = new Random(System.currentTimeMillis()); 384 StringBuilder randomString = new StringBuilder(1979); 385 for (int i = 0; i < 1979; i++) { 386 randomString.append((random.nextInt() & 0xf) % 10); 387 } 388 389 // if cursor window size changed, adjust this value too 390 final int count = 600; 391 for (int i = 0; i < count; i++) { 392 StringBuilder sql = new StringBuilder(2100); 393 sql.append("INSERT INTO test (txt, data) VALUES ('"); 394 sql.append(randomString); 395 sql.append("','"); 396 sql.append(i); 397 sql.append("');"); 398 mDatabase.execSQL(sql.toString()); 399 } 400 401 Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "txt", "data" }, 402 null, null, null, null, null)); 403 assertNotNull(testCursor); 404 405 int i = 0; 406 while (testCursor.moveToNext()) { 407 assertEquals(randomString.toString(), testCursor.getString(0)); 408 assertEquals(i, testCursor.getInt(1)); 409 i++; 410 } 411 assertEquals(count, i); 412 assertEquals(count, testCursor.getCount()); 413 testCursor.close(); 414 } 415 416 @MediumTest 417 public void testRequery() throws Exception { 418 populateDefaultTable(); 419 420 Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT * FROM test", null)); 421 assertNotNull(testCursor); 422 assertEquals(3, testCursor.getCount()); 423 testCursor.deactivate(); 424 testCursor.requery(); 425 assertEquals(3, testCursor.getCount()); 426 testCursor.close(); 427 } 428 429 @MediumTest 430 public void testRequeryWithSelection() throws Exception { 431 populateDefaultTable(); 432 433 Cursor testCursor = getTestCursor( 434 mDatabase.rawQuery("SELECT data FROM test WHERE data = '" + sString1 + "'", 435 null)); 436 assertNotNull(testCursor); 437 assertEquals(1, testCursor.getCount()); 438 assertTrue(testCursor.moveToFirst()); 439 assertEquals(sString1, testCursor.getString(0)); 440 testCursor.deactivate(); 441 testCursor.requery(); 442 assertEquals(1, testCursor.getCount()); 443 assertTrue(testCursor.moveToFirst()); 444 assertEquals(sString1, testCursor.getString(0)); 445 testCursor.close(); 446 } 447 448 @MediumTest 449 public void testRequeryWithSelectionArgs() throws Exception { 450 populateDefaultTable(); 451 452 Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT data FROM test WHERE data = ?", 453 new String[] { sString1 })); 454 assertNotNull(testCursor); 455 assertEquals(1, testCursor.getCount()); 456 assertTrue(testCursor.moveToFirst()); 457 assertEquals(sString1, testCursor.getString(0)); 458 testCursor.deactivate(); 459 testCursor.requery(); 460 assertEquals(1, testCursor.getCount()); 461 assertTrue(testCursor.moveToFirst()); 462 assertEquals(sString1, testCursor.getString(0)); 463 testCursor.close(); 464 } 465 466 @MediumTest 467 public void testRequeryWithAlteredSelectionArgs() throws Exception { 468 /** 469 * Test the ability of a subclass of SQLiteCursor to change its query arguments. 470 */ 471 populateDefaultTable(); 472 473 SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() { 474 public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, 475 String editTable, SQLiteQuery query) { 476 return new SQLiteCursor(db, masterQuery, editTable, query) { 477 @Override 478 public boolean requery() { 479 setSelectionArguments(new String[] { "2" }); 480 return super.requery(); 481 } 482 }; 483 } 484 }; 485 Cursor testCursor = getTestCursor(mDatabase.rawQueryWithFactory(factory, 486 "SELECT data FROM test WHERE _id <= ?", 487 new String[] { "1" }, null)); 488 assertNotNull(testCursor); 489 assertEquals(1, testCursor.getCount()); 490 assertTrue(testCursor.moveToFirst()); 491 assertEquals(sString1, testCursor.getString(0)); 492 493 // Our hacked requery() changes the query arguments in the cursor. 494 testCursor.requery(); 495 496 assertEquals(2, testCursor.getCount()); 497 assertTrue(testCursor.moveToFirst()); 498 assertEquals(sString1, testCursor.getString(0)); 499 assertTrue(testCursor.moveToNext()); 500 assertEquals(sString2, testCursor.getString(0)); 501 502 // Test that setting query args on a deactivated cursor also works. 503 testCursor.deactivate(); 504 testCursor.requery(); 505 } 506 } 507