1 /* 2 * Copyright (C) 2006 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; 18 19 import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_COLUMNNAME_INDEX; 20 import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_DEFAULT_INDEX; 21 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.database.sqlite.SQLiteDatabase; 25 import android.database.sqlite.SQLiteDebug; 26 import android.database.sqlite.SQLiteException; 27 import android.os.Parcel; 28 import android.test.AndroidTestCase; 29 import android.test.PerformanceTestCase; 30 import android.util.Log; 31 import android.util.Pair; 32 33 import androidx.test.InstrumentationRegistry; 34 import androidx.test.filters.LargeTest; 35 import androidx.test.filters.MediumTest; 36 import androidx.test.filters.SmallTest; 37 import androidx.test.uiautomator.UiDevice; 38 39 import junit.framework.Assert; 40 41 import java.io.File; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.List; 45 import java.util.Locale; 46 47 /** 48 * Usage: bit FrameworksCoreTests:android.database.DatabaseGeneralTest 49 */ 50 public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceTestCase { 51 private static final String TAG = "DatabaseGeneralTest"; 52 53 private static final String sString1 = "this is a test"; 54 private static final String sString2 = "and yet another test"; 55 private static final String sString3 = "this string is a little longer, but still a test"; 56 private static final String PHONE_NUMBER = "16175551212"; 57 58 private static final int CURRENT_DATABASE_VERSION = 42; 59 private SQLiteDatabase mDatabase; 60 private File mDatabaseFile; 61 62 @Override setUp()63 protected void setUp() throws Exception { 64 super.setUp(); 65 File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE); 66 mDatabaseFile = new File(dbDir, "database_test.db"); 67 if (mDatabaseFile.exists()) { 68 mDatabaseFile.delete(); 69 } 70 mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null); 71 assertNotNull(mDatabase); 72 mDatabase.setVersion(CURRENT_DATABASE_VERSION); 73 } 74 75 @Override tearDown()76 protected void tearDown() throws Exception { 77 mDatabase.close(); 78 SQLiteDatabase.deleteDatabase(mDatabaseFile); 79 super.tearDown(); 80 } 81 isPerformanceOnly()82 public boolean isPerformanceOnly() { 83 return false; 84 } 85 86 // These test can only be run once. startPerformance(Intermediates intermediates)87 public int startPerformance(Intermediates intermediates) { 88 return 1; 89 } 90 populateDefaultTable()91 private void populateDefaultTable() { 92 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);"); 93 94 mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');"); 95 mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');"); 96 mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');"); 97 } 98 99 @MediumTest testVersion()100 public void testVersion() throws Exception { 101 assertEquals(CURRENT_DATABASE_VERSION, mDatabase.getVersion()); 102 mDatabase.setVersion(11); 103 assertEquals(11, mDatabase.getVersion()); 104 } 105 106 @MediumTest testUpdate()107 public void testUpdate() throws Exception { 108 populateDefaultTable(); 109 110 ContentValues values = new ContentValues(1); 111 values.put("data", "this is an updated test"); 112 assertEquals(1, mDatabase.update("test", values, "_id=1", null)); 113 Cursor c = mDatabase.query("test", null, "_id=1", null, null, null, null); 114 assertNotNull(c); 115 assertEquals(1, c.getCount()); 116 c.moveToFirst(); 117 String value = c.getString(c.getColumnIndexOrThrow("data")); 118 assertEquals("this is an updated test", value); 119 } 120 121 @MediumTest testPhoneNumbersEqual()122 public void testPhoneNumbersEqual() throws Exception { 123 mDatabase.execSQL("CREATE TABLE phones (num TEXT);"); 124 mDatabase.execSQL("INSERT INTO phones (num) VALUES ('911');"); 125 mDatabase.execSQL("INSERT INTO phones (num) VALUES ('5555');"); 126 mDatabase.execSQL("INSERT INTO phones (num) VALUES ('+" + PHONE_NUMBER + "');"); 127 128 String number; 129 Cursor c; 130 131 c = mDatabase.query("phones", null, 132 "PHONE_NUMBERS_EQUAL(num, '504-555-7683')", null, null, null, null); 133 assertTrue(c == null || c.getCount() == 0); 134 c.close(); 135 136 c = mDatabase.query("phones", null, 137 "PHONE_NUMBERS_EQUAL(num, '911')", null, null, null, null); 138 assertNotNull(c); 139 assertEquals(1, c.getCount()); 140 c.moveToFirst(); 141 number = c.getString(c.getColumnIndexOrThrow("num")); 142 assertEquals("911", number); 143 c.close(); 144 145 c = mDatabase.query("phones", null, 146 "PHONE_NUMBERS_EQUAL(num, '5555')", null, null, null, null); 147 assertNotNull(c); 148 assertEquals(1, c.getCount()); 149 c.moveToFirst(); 150 number = c.getString(c.getColumnIndexOrThrow("num")); 151 assertEquals("5555", number); 152 c.close(); 153 154 c = mDatabase.query("phones", null, 155 "PHONE_NUMBERS_EQUAL(num, '180055555555')", null, null, null, null); 156 assertTrue(c == null || c.getCount() == 0); 157 c.close(); 158 159 c = mDatabase.query("phones", null, 160 "PHONE_NUMBERS_EQUAL(num, '+" + PHONE_NUMBER + "')", null, null, null, null); 161 assertNotNull(c); 162 assertEquals(1, c.getCount()); 163 c.moveToFirst(); 164 number = c.getString(c.getColumnIndexOrThrow("num")); 165 assertEquals("+" + PHONE_NUMBER, number); 166 c.close(); 167 168 c = mDatabase.query("phones", null, 169 "PHONE_NUMBERS_EQUAL(num, '+1 (617).555-1212')", null, null, null, null); 170 assertNotNull(c); 171 assertEquals(1, c.getCount()); 172 c.moveToFirst(); 173 number = c.getString(c.getColumnIndexOrThrow("num")); 174 assertEquals("+" + PHONE_NUMBER, number); 175 c.close(); 176 177 c = mDatabase.query("phones", null, 178 "PHONE_NUMBERS_EQUAL(num, '" + PHONE_NUMBER + "')", null, null, null, null); 179 assertNotNull(c); 180 assertEquals(1, c.getCount()); 181 c.moveToFirst(); 182 number = c.getString(c.getColumnIndexOrThrow("num")); 183 assertEquals("+" + PHONE_NUMBER, number); 184 c.close(); 185 186 /* 187 c = mDatabase.query("phones", null, 188 "PHONE_NUMBERS_EQUAL(num, '5551212')", null, null, null, null); 189 assertNotNull(c); 190 assertEquals(1, c.getCount()); 191 c.moveToFirst(); 192 number = c.getString(c.getColumnIndexOrThrow("num")); 193 assertEquals("+" + PHONE_NUMBER, number); 194 c.close(); 195 */ 196 197 c = mDatabase.query("phones", null, 198 "PHONE_NUMBERS_EQUAL(num, '011" + PHONE_NUMBER + "')", null, null, null, null); 199 assertNotNull(c); 200 assertEquals(1, c.getCount()); 201 c.moveToFirst(); 202 number = c.getString(c.getColumnIndexOrThrow("num")); 203 assertEquals("+" + PHONE_NUMBER, number); 204 c.close(); 205 206 c = mDatabase.query("phones", null, 207 "PHONE_NUMBERS_EQUAL(num, '00" + PHONE_NUMBER + "')", null, null, null, null); 208 assertNotNull(c); 209 assertEquals(1, c.getCount()); 210 c.moveToFirst(); 211 number = c.getString(c.getColumnIndexOrThrow("num")); 212 assertEquals("+" + PHONE_NUMBER, number); 213 c.close(); 214 } 215 phoneNumberCompare(String phone1, String phone2, boolean equal, boolean useStrictComparation)216 private void phoneNumberCompare(String phone1, String phone2, boolean equal, 217 boolean useStrictComparation) { 218 String[] temporalPhoneNumbers = new String[2]; 219 temporalPhoneNumbers[0] = phone1; 220 temporalPhoneNumbers[1] = phone2; 221 222 Cursor cursor = mDatabase.rawQuery( 223 String.format( 224 "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?, %d) " + 225 "THEN 'equal' ELSE 'not equal' END", 226 (useStrictComparation ? 1 : 0)), 227 temporalPhoneNumbers); 228 try { 229 assertNotNull(cursor); 230 assertTrue(cursor.moveToFirst()); 231 if (equal) { 232 assertEquals(String.format("Unexpectedly, \"%s != %s\".", phone1, phone2), 233 "equal", cursor.getString(0)); 234 } else { 235 assertEquals(String.format("Unexpectedly, \"%s\" == \"%s\".", phone1, phone2), 236 "not equal", cursor.getString(0)); 237 } 238 } finally { 239 if (cursor != null) { 240 cursor.close(); 241 } 242 } 243 } 244 assertPhoneNumberEqual(String phone1, String phone2)245 private void assertPhoneNumberEqual(String phone1, String phone2) throws Exception { 246 assertPhoneNumberEqual(phone1, phone2, true); 247 assertPhoneNumberEqual(phone1, phone2, false); 248 } 249 assertPhoneNumberEqual(String phone1, String phone2, boolean useStrict)250 private void assertPhoneNumberEqual(String phone1, String phone2, boolean useStrict) 251 throws Exception { 252 phoneNumberCompare(phone1, phone2, true, useStrict); 253 } 254 assertPhoneNumberNotEqual(String phone1, String phone2)255 private void assertPhoneNumberNotEqual(String phone1, String phone2) throws Exception { 256 assertPhoneNumberNotEqual(phone1, phone2, true); 257 assertPhoneNumberNotEqual(phone1, phone2, false); 258 } 259 assertPhoneNumberNotEqual(String phone1, String phone2, boolean useStrict)260 private void assertPhoneNumberNotEqual(String phone1, String phone2, boolean useStrict) 261 throws Exception { 262 phoneNumberCompare(phone1, phone2, false, useStrict); 263 } 264 265 /** 266 * Tests international matching issues for the PHONE_NUMBERS_EQUAL function. 267 * 268 * @throws Exception 269 */ 270 @SmallTest testPhoneNumbersEqualInternational()271 public void testPhoneNumbersEqualInternational() throws Exception { 272 assertPhoneNumberEqual("1", "1"); 273 assertPhoneNumberEqual("123123", "123123"); 274 assertPhoneNumberNotEqual("123123", "923123"); 275 assertPhoneNumberNotEqual("123123", "123129"); 276 assertPhoneNumberNotEqual("123123", "1231234"); 277 assertPhoneNumberNotEqual("123123", "0123123", false); 278 assertPhoneNumberNotEqual("123123", "0123123", true); 279 assertPhoneNumberEqual("650-253-0000", "6502530000"); 280 assertPhoneNumberEqual("650-253-0000", "650 253 0000"); 281 assertPhoneNumberEqual("650 253 0000", "6502530000"); 282 assertPhoneNumberEqual("+1 650-253-0000", "6502530000"); 283 assertPhoneNumberEqual("001 650-253-0000", "6502530000"); 284 assertPhoneNumberEqual("0111 650-253-0000", "6502530000"); 285 286 // Russian trunk digit 287 assertPhoneNumberEqual("+79161234567", "89161234567"); 288 289 // French trunk digit 290 assertPhoneNumberEqual("+33123456789", "0123456789"); 291 292 // Hungarian two digit trunk (currently only works for loose comparison) 293 assertPhoneNumberEqual("+36 1 234 5678", "06 1234-5678", false); 294 295 // Mexican two digit trunk (currently only works for loose comparison) 296 assertPhoneNumberEqual("+52 55 1234 5678", "01 55 1234 5678", false); 297 298 // Mongolian two digit trunk (currently only works for loose comparison) 299 assertPhoneNumberEqual("+976 1 123 4567", "01 1 23 4567", false); 300 assertPhoneNumberEqual("+976 2 234 5678", "02 2 34 5678", false); 301 302 // Trunk digit for city codes in the Netherlands 303 assertPhoneNumberEqual("+31771234567", "0771234567"); 304 305 // Test broken caller ID seen on call from Thailand to the US 306 assertPhoneNumberEqual("+66811234567", "166811234567"); 307 308 // Test the same in-country number with different country codes 309 assertPhoneNumberNotEqual("+33123456789", "+1123456789"); 310 311 // Test one number with country code and the other without 312 assertPhoneNumberEqual("5125551212", "+15125551212"); 313 314 // Test two NANP numbers that only differ in the area code 315 assertPhoneNumberNotEqual("5125551212", "6505551212"); 316 317 // Japanese phone numbers 318 assertPhoneNumberEqual("090-1234-5678", "+819012345678"); 319 assertPhoneNumberEqual("090(1234)5678", "+819012345678"); 320 assertPhoneNumberEqual("090-1234-5678", "+81-90-1234-5678"); 321 322 // Equador 323 assertPhoneNumberEqual("+593(800)123-1234", "8001231234"); 324 assertPhoneNumberEqual("+593-2-1234-123", "21234123"); 325 326 // Two continuous 0 at the beginning of the phone string should not be 327 // treated as trunk prefix in the strict comparation. 328 assertPhoneNumberEqual("008001231234", "8001231234", false); 329 assertPhoneNumberNotEqual("008001231234", "8001231234", true); 330 331 // Confirm that the bug found before does not re-appear 332 assertPhoneNumberNotEqual("080-1234-5678", "+819012345678"); 333 334 // Wrong prefix for Japan (currently only works for loose comparison) 335 assertPhoneNumberNotEqual("290-1234-5678", "+819012345678", false); 336 assertPhoneNumberNotEqual("+819012345678", "290-1234-5678", false); 337 338 // Wrong prefix for USA 339 assertPhoneNumberNotEqual("550-450-3605", "+14504503605"); 340 assertPhoneNumberNotEqual("550-450-3605", "+15404503605"); 341 assertPhoneNumberNotEqual("550-450-3605", "+15514503605"); 342 assertPhoneNumberNotEqual("5504503605", "+14504503605"); 343 assertPhoneNumberNotEqual("+14504503605", "550-450-3605"); 344 assertPhoneNumberNotEqual("+15404503605", "550-450-3605"); 345 assertPhoneNumberNotEqual("+15514503605", "550-450-3605"); 346 assertPhoneNumberNotEqual("+14504503605", "5504503605"); 347 } 348 349 @MediumTest testCopyString()350 public void testCopyString() throws Exception { 351 mDatabase.execSQL("CREATE TABLE guess (numi INTEGER, numf FLOAT, str TEXT);"); 352 mDatabase.execSQL( 353 "INSERT INTO guess (numi,numf,str) VALUES (0,0.0,'ZoomZoomZoomZoom');"); 354 mDatabase.execSQL("INSERT INTO guess (numi,numf,str) VALUES (2000000000,3.1415926535,'');"); 355 String chinese = "\u4eac\u4ec5 \u5c3d\u5f84\u60ca"; 356 String[] arr = new String[1]; 357 arr[0] = chinese; 358 mDatabase.execSQL("INSERT INTO guess (numi,numf,str) VALUES (-32768,-1.0,?)", arr); 359 360 Cursor c; 361 362 c = mDatabase.rawQuery("SELECT * FROM guess", null); 363 364 c.moveToFirst(); 365 366 CharArrayBuffer buf = new CharArrayBuffer(14); 367 368 String compareTo = c.getString(c.getColumnIndexOrThrow("numi")); 369 int numiIdx = c.getColumnIndexOrThrow("numi"); 370 int numfIdx = c.getColumnIndexOrThrow("numf"); 371 int strIdx = c.getColumnIndexOrThrow("str"); 372 373 c.copyStringToBuffer(numiIdx, buf); 374 assertEquals(1, buf.sizeCopied); 375 assertEquals(compareTo, new String(buf.data, 0, buf.sizeCopied)); 376 377 c.copyStringToBuffer(strIdx, buf); 378 assertEquals("ZoomZoomZoomZoom", new String(buf.data, 0, buf.sizeCopied)); 379 380 c.moveToNext(); 381 compareTo = c.getString(numfIdx); 382 383 c.copyStringToBuffer(numfIdx, buf); 384 assertEquals(compareTo, new String(buf.data, 0, buf.sizeCopied)); 385 c.copyStringToBuffer(strIdx, buf); 386 assertEquals(0, buf.sizeCopied); 387 388 c.moveToNext(); 389 c.copyStringToBuffer(numfIdx, buf); 390 assertEquals(-1.0, Double.valueOf( 391 new String(buf.data, 0, buf.sizeCopied)).doubleValue()); 392 393 c.copyStringToBuffer(strIdx, buf); 394 compareTo = c.getString(strIdx); 395 assertEquals(chinese, compareTo); 396 397 assertEquals(chinese, new String(buf.data, 0, buf.sizeCopied)); 398 c.close(); 399 } 400 401 @MediumTest testSchemaChange1()402 public void testSchemaChange1() throws Exception { 403 SQLiteDatabase db1 = mDatabase; 404 Cursor cursor; 405 406 db1.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);"); 407 408 cursor = db1.query("db1", null, null, null, null, null, null); 409 assertNotNull("Cursor is null", cursor); 410 411 db1.execSQL("CREATE TABLE db2 (_id INTEGER PRIMARY KEY, data TEXT);"); 412 413 assertEquals(0, cursor.getCount()); 414 cursor.deactivate(); 415 } 416 417 @MediumTest testSchemaChange2()418 public void testSchemaChange2() throws Exception { 419 mDatabase.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);"); 420 Cursor cursor = mDatabase.query("db1", null, null, null, null, null, null); 421 assertNotNull(cursor); 422 assertEquals(0, cursor.getCount()); 423 cursor.close(); 424 } 425 426 @MediumTest testSchemaChange3()427 public void testSchemaChange3() throws Exception { 428 mDatabase.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);"); 429 mDatabase.execSQL("INSERT INTO db1 (data) VALUES ('test');"); 430 mDatabase.execSQL("ALTER TABLE db1 ADD COLUMN blah int;"); 431 Cursor c = null; 432 try { 433 c = mDatabase.rawQuery("select blah from db1", null); 434 } catch (SQLiteException e) { 435 fail("unexpected exception: " + e.getMessage()); 436 } finally { 437 if (c != null) { 438 c.close(); 439 } 440 } 441 } 442 443 @MediumTest testSelectionArgs()444 public void testSelectionArgs() throws Exception { 445 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);"); 446 ContentValues values = new ContentValues(1); 447 values.put("data", "don't forget to handled 's"); 448 mDatabase.insert("test", "data", values); 449 values.clear(); 450 values.put("data", "no apostrophes here"); 451 mDatabase.insert("test", "data", values); 452 Cursor c = mDatabase.query( 453 "test", null, "data GLOB ?", new String[]{"*'*"}, null, null, null); 454 assertEquals(1, c.getCount()); 455 assertTrue(c.moveToFirst()); 456 assertEquals("don't forget to handled 's", c.getString(1)); 457 c.deactivate(); 458 459 // make sure code should checking null string properly so that 460 // it won't crash 461 try { 462 mDatabase.query("test", new String[]{"_id"}, 463 "_id=?", new String[]{null}, null, null, null); 464 fail("expected exception not thrown"); 465 } catch (IllegalArgumentException e) { 466 // expected 467 } 468 } 469 470 @MediumTest testTransactions()471 public void testTransactions() throws Exception { 472 mDatabase.execSQL("CREATE TABLE test (num INTEGER);"); 473 mDatabase.execSQL("INSERT INTO test (num) VALUES (0)"); 474 475 // Make sure that things work outside an explicit transaction. 476 setNum(1); 477 checkNum(1); 478 479 // Test a single-level transaction. 480 setNum(0); 481 mDatabase.beginTransaction(); 482 setNum(1); 483 mDatabase.setTransactionSuccessful(); 484 mDatabase.endTransaction(); 485 checkNum(1); 486 Assert.assertFalse(mDatabase.isDbLockedByCurrentThread()); 487 488 // Test a rolled-back transaction. 489 setNum(0); 490 mDatabase.beginTransaction(); 491 setNum(1); 492 mDatabase.endTransaction(); 493 checkNum(0); 494 Assert.assertFalse(mDatabase.isDbLockedByCurrentThread()); 495 496 // We should get an error if we end a non-existent transaction. 497 assertThrowsIllegalState(new Runnable() { public void run() { 498 mDatabase.endTransaction(); 499 }}); 500 501 // We should get an error if a set a non-existent transaction as clean. 502 assertThrowsIllegalState(new Runnable() { public void run() { 503 mDatabase.setTransactionSuccessful(); 504 }}); 505 506 mDatabase.beginTransaction(); 507 mDatabase.setTransactionSuccessful(); 508 // We should get an error if we mark a transaction as clean twice. 509 assertThrowsIllegalState(new Runnable() { public void run() { 510 mDatabase.setTransactionSuccessful(); 511 }}); 512 // We should get an error if we begin a transaction after marking the parent as clean. 513 assertThrowsIllegalState(new Runnable() { public void run() { 514 mDatabase.beginTransaction(); 515 }}); 516 mDatabase.endTransaction(); 517 Assert.assertFalse(mDatabase.isDbLockedByCurrentThread()); 518 519 // Test a two-level transaction. 520 setNum(0); 521 mDatabase.beginTransaction(); 522 mDatabase.beginTransaction(); 523 setNum(1); 524 mDatabase.setTransactionSuccessful(); 525 mDatabase.endTransaction(); 526 mDatabase.setTransactionSuccessful(); 527 mDatabase.endTransaction(); 528 checkNum(1); 529 Assert.assertFalse(mDatabase.isDbLockedByCurrentThread()); 530 531 // Test rolling back an inner transaction. 532 setNum(0); 533 mDatabase.beginTransaction(); 534 mDatabase.beginTransaction(); 535 setNum(1); 536 mDatabase.endTransaction(); 537 mDatabase.setTransactionSuccessful(); 538 mDatabase.endTransaction(); 539 checkNum(0); 540 Assert.assertFalse(mDatabase.isDbLockedByCurrentThread()); 541 542 // Test rolling back an outer transaction. 543 setNum(0); 544 mDatabase.beginTransaction(); 545 mDatabase.beginTransaction(); 546 setNum(1); 547 mDatabase.setTransactionSuccessful(); 548 mDatabase.endTransaction(); 549 mDatabase.endTransaction(); 550 checkNum(0); 551 Assert.assertFalse(mDatabase.isDbLockedByCurrentThread()); 552 } 553 setNum(int num)554 private void setNum(int num) { 555 mDatabase.execSQL("UPDATE test SET num = " + num); 556 } 557 checkNum(int num)558 private void checkNum(int num) { 559 Assert.assertEquals( 560 num, DatabaseUtils.longForQuery(mDatabase, "SELECT num FROM test", null)); 561 } 562 assertThrowsIllegalState(Runnable r)563 private void assertThrowsIllegalState(Runnable r) { 564 boolean ok = false; 565 try { 566 r.run(); 567 } catch (IllegalStateException e) { 568 ok = true; 569 } 570 Assert.assertTrue(ok); 571 } 572 573 // Disable these until we can explicitly mark them as stress tests xxtestMem1()574 public void xxtestMem1() throws Exception { 575 populateDefaultTable(); 576 577 for (int i = 0; i < 50000; i++) { 578 Cursor cursor = mDatabase.query("test", null, null, null, null, null, null); 579 cursor.moveToFirst(); 580 cursor.close(); 581 // Log.i("~~~~", "Finished round " + i); 582 } 583 } 584 585 // Disable these until we can explicitly mark them as stress tests xxtestMem2()586 public void xxtestMem2() throws Exception { 587 populateDefaultTable(); 588 589 for (int i = 0; i < 50000; i++) { 590 Cursor cursor = mDatabase.query("test", null, null, null, null, null, null); 591 cursor.close(); 592 // Log.i("~~~~", "Finished round " + i); 593 } 594 } 595 596 // Disable these until we can explicitly mark them as stress tests xxtestMem3()597 public void xxtestMem3() throws Exception { 598 populateDefaultTable(); 599 600 for (int i = 0; i < 50000; i++) { 601 Cursor cursor = mDatabase.query("test", null, null, null, null, null, null); 602 cursor.deactivate(); 603 // Log.i("~~~~", "Finished round " + i); 604 } 605 } 606 607 @MediumTest testContentValues()608 public void testContentValues() throws Exception { 609 ContentValues values = new ContentValues(); 610 values.put("string", "value"); 611 assertEquals("value", values.getAsString("string")); 612 byte[] bytes = new byte[42]; 613 Arrays.fill(bytes, (byte) 0x28); 614 values.put("byteArray", bytes); 615 assertTrue(Arrays.equals(bytes, values.getAsByteArray("byteArray"))); 616 617 // Write the ContentValues to a Parcel and then read them out 618 Parcel p = Parcel.obtain(); 619 values.writeToParcel(p, 0); 620 p.setDataPosition(0); 621 values = ContentValues.CREATOR.createFromParcel(p); 622 623 // Read the values out again and make sure they're the same 624 assertTrue(Arrays.equals(bytes, values.getAsByteArray("byteArray"))); 625 assertEquals("value", values.get("string")); 626 } 627 628 @MediumTest testTableInfoPragma()629 public void testTableInfoPragma() throws Exception { 630 mDatabase.execSQL("CREATE TABLE pragma_test (" + 631 "i INTEGER DEFAULT 1234, " + 632 "j INTEGER, " + 633 "s TEXT DEFAULT 'hello', " + 634 "t TEXT, " + 635 "'select' TEXT DEFAULT \"hello\")"); 636 try { 637 Cursor cur = mDatabase.rawQuery("PRAGMA table_info(pragma_test)", null); 638 Assert.assertEquals(5, cur.getCount()); 639 640 Assert.assertTrue(cur.moveToNext()); 641 Assert.assertEquals("i", 642 cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX)); 643 Assert.assertEquals("1234", 644 cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX)); 645 646 Assert.assertTrue(cur.moveToNext()); 647 Assert.assertEquals("j", 648 cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX)); 649 Assert.assertNull(cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX)); 650 651 Assert.assertTrue(cur.moveToNext()); 652 Assert.assertEquals("s", 653 cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX)); 654 Assert.assertEquals("'hello'", 655 cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX)); 656 657 Assert.assertTrue(cur.moveToNext()); 658 Assert.assertEquals("t", 659 cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX)); 660 Assert.assertNull(cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX)); 661 662 Assert.assertTrue(cur.moveToNext()); 663 Assert.assertEquals("select", 664 cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX)); 665 Assert.assertEquals("\"hello\"", 666 cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX)); 667 668 cur.close(); 669 } catch (Throwable t) { 670 throw new RuntimeException( 671 "If you see this test fail, it's likely that something about " + 672 "sqlite's PRAGMA table_info(...) command has changed.", t); 673 } 674 } 675 676 @MediumTest testInsertHelper()677 public void testInsertHelper() throws Exception { 678 Cursor cur; 679 ContentValues cv; 680 long row; 681 682 mDatabase.execSQL("CREATE TABLE insert_test (" + 683 "_id INTEGER PRIMARY KEY, " + 684 "s TEXT NOT NULL UNIQUE, " + 685 "t TEXT NOT NULL DEFAULT 'hello world', " + 686 "i INTEGER, " + 687 "j INTEGER NOT NULL DEFAULT 1234, " + 688 "'select' TEXT)"); 689 690 DatabaseUtils.InsertHelper ih = 691 new DatabaseUtils.InsertHelper(mDatabase, "insert_test"); 692 693 cv = new ContentValues(); 694 cv.put("s", "one"); 695 row = ih.insert(cv); 696 cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null); 697 Assert.assertTrue(cur.moveToFirst()); 698 Assert.assertEquals("one", cur.getString(1)); 699 Assert.assertEquals("hello world", cur.getString(2)); 700 Assert.assertNull(cur.getString(3)); 701 Assert.assertEquals(1234, cur.getLong(4)); 702 Assert.assertNull(cur.getString(5)); 703 cur.close(); 704 705 cv = new ContentValues(); 706 cv.put("s", "two"); 707 cv.put("t", "goodbye world"); 708 row = ih.insert(cv); 709 cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null); 710 Assert.assertTrue(cur.moveToFirst()); 711 Assert.assertEquals("two", cur.getString(1)); 712 Assert.assertEquals("goodbye world", cur.getString(2)); 713 Assert.assertNull(cur.getString(3)); 714 Assert.assertEquals(1234, cur.getLong(4)); 715 Assert.assertNull(cur.getString(5)); 716 cur.close(); 717 718 cv = new ContentValues(); 719 cv.put("t", "goodbye world"); 720 row = ih.insert(cv); 721 Assert.assertEquals(-1, row); 722 723 cv = new ContentValues(); 724 cv.put("s", "three"); 725 cv.put("i", 2345); 726 cv.put("j", 3456); 727 cv.put("select", "tricky"); 728 row = ih.insert(cv); 729 cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null); 730 Assert.assertTrue(cur.moveToFirst()); 731 Assert.assertEquals("three", cur.getString(1)); 732 Assert.assertEquals("hello world", cur.getString(2)); 733 Assert.assertEquals(2345, cur.getLong(3)); 734 Assert.assertEquals(3456, cur.getLong(4)); 735 Assert.assertEquals("tricky", cur.getString(5)); 736 cur.close(); 737 738 cv = new ContentValues(); 739 cv.put("s", "three"); 740 cv.put("i", 6789); 741 row = ih.insert(cv); 742 Assert.assertEquals(-1, row); 743 row = ih.replace(cv); 744 cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null); 745 Assert.assertTrue(cur.moveToFirst()); 746 Assert.assertEquals("three", cur.getString(1)); 747 Assert.assertEquals("hello world", cur.getString(2)); 748 Assert.assertEquals(6789, cur.getLong(3)); 749 cur.close(); 750 751 ih.close(); 752 } 753 754 @MediumTest testSemicolonsInStatements()755 public void testSemicolonsInStatements() throws Exception { 756 mDatabase.execSQL("CREATE TABLE pragma_test (" + 757 "i INTEGER DEFAULT 1234, " + 758 "j INTEGER, " + 759 "s TEXT DEFAULT 'hello', " + 760 "t TEXT, " + 761 "'select' TEXT DEFAULT \"hello\")"); 762 try { 763 // ending the sql statement with semicolons shouldn't be a problem. 764 Cursor cur = mDatabase.rawQuery("PRAGMA database_list;", null); 765 cur.close(); 766 // two semicolons in the statement shouldn't be a problem. 767 cur = mDatabase.rawQuery("PRAGMA database_list;;", null); 768 cur.close(); 769 } catch (Throwable t) { 770 fail("unexpected, of course"); 771 } 772 } 773 774 @MediumTest testUnionsWithBindArgs()775 public void testUnionsWithBindArgs() { 776 /* make sure unions with bindargs work http://b/issue?id=1061291 */ 777 mDatabase.execSQL("CREATE TABLE A (i int);"); 778 mDatabase.execSQL("create table B (k int);"); 779 mDatabase.execSQL("create table C (n int);"); 780 mDatabase.execSQL("insert into A values(1);"); 781 mDatabase.execSQL("insert into A values(2);"); 782 mDatabase.execSQL("insert into A values(3);"); 783 mDatabase.execSQL("insert into B values(201);"); 784 mDatabase.execSQL("insert into B values(202);"); 785 mDatabase.execSQL("insert into B values(203);"); 786 mDatabase.execSQL("insert into C values(901);"); 787 mDatabase.execSQL("insert into C values(902);"); 788 String s = "select i from A where i > 2 " + 789 "UNION select k from B where k > 201 " + 790 "UNION select n from C where n !=900;"; 791 Cursor c = mDatabase.rawQuery(s, null); 792 int n = c.getCount(); 793 c.close(); 794 String s1 = "select i from A where i > ? " + 795 "UNION select k from B where k > ? " + 796 "UNION select n from C where n != ?;"; 797 Cursor c1 = mDatabase.rawQuery(s1, new String[]{"2", "201", "900"}); 798 assertEquals(n, c1.getCount()); 799 c1.close(); 800 } 801 802 /** 803 * This test is available only when the platform has a locale with the language "ja". 804 * It finishes without failure when it is not available. 805 */ 806 @MediumTest testCollateLocalizedForJapanese()807 public void testCollateLocalizedForJapanese() throws Exception { 808 final String testName = "DatabaseGeneralTest#testCollateLocalizedForJapanese()"; 809 final Locale[] localeArray = Locale.getAvailableLocales(); 810 final String japanese = Locale.JAPANESE.getLanguage(); 811 final String english = Locale.ENGLISH.getLanguage(); 812 Locale japaneseLocale = null; 813 Locale englishLocale = null; 814 for (Locale locale : localeArray) { 815 if (locale != null) { 816 final String language = locale.getLanguage(); 817 if (language == null) { 818 continue; 819 } else if (language.equals(japanese)) { 820 japaneseLocale = locale; 821 } else if (language.equals(english)) { 822 englishLocale = locale; 823 } 824 } 825 826 if (japaneseLocale != null && englishLocale != null) { 827 break; 828 } 829 } 830 831 if (japaneseLocale == null || englishLocale == null) { 832 Log.d(TAG, testName + "n is silently skipped since " + 833 (englishLocale == null ? 834 (japaneseLocale == null ? 835 "Both English and Japanese locales do not exist." : 836 "English locale does not exist.") : 837 (japaneseLocale == null ? 838 "Japanese locale does not exist." : 839 "...why?"))); 840 return; 841 } 842 843 Locale originalLocale = Locale.getDefault(); 844 try { 845 846 final String dbName = "collate_localized_test"; 847 mDatabase.execSQL("CREATE TABLE " + dbName + " (" + 848 "_id INTEGER PRIMARY KEY, " + 849 "s TEXT COLLATE LOCALIZED) "); 850 DatabaseUtils.InsertHelper ih = 851 new DatabaseUtils.InsertHelper(mDatabase, dbName); 852 ContentValues cv = new ContentValues(); 853 854 cv = new ContentValues(); // 855 cv.put("s", "\uFF75\uFF77\uFF85\uFF9C"); // O-ki-na-wa in half-width Katakana 856 ih.insert(cv); 857 858 cv = new ContentValues(); // 859 cv.put("s", "\u306B\u307B\u3093"); // Ni-ho-n in Hiragana 860 ih.insert(cv); 861 862 cv = new ContentValues(); // 863 cv.put("s", "\u30A2\u30E1\u30EA\u30AB"); // A-me-ri-ca in hull-width Katakana 864 ih.insert(cv); 865 866 // Assume setLocale() does REINDEX and an English locale does not consider 867 // Japanese-specific LOCALIZED order. 868 Locale.setDefault(englishLocale); 869 Locale.setDefault(japaneseLocale); 870 871 Cursor cur = mDatabase.rawQuery( 872 "SELECT * FROM " + dbName + " ORDER BY s", null); 873 assertTrue(cur.moveToFirst()); 874 assertEquals("\u30A2\u30E1\u30EA\u30AB", cur.getString(1)); 875 assertTrue(cur.moveToNext()); 876 assertEquals("\uFF75\uFF77\uFF85\uFF9C", cur.getString(1)); 877 assertTrue(cur.moveToNext()); 878 assertEquals("\u306B\u307B\u3093", cur.getString(1)); 879 } finally { 880 if (originalLocale != null) { 881 try { 882 Locale.setDefault(originalLocale); 883 } catch (Exception e) { 884 } 885 } 886 } 887 } 888 889 @SmallTest testSetMaxCahesize()890 public void testSetMaxCahesize() { 891 mDatabase.execSQL("CREATE TABLE test (i int, j int);"); 892 mDatabase.execSQL("insert into test values(1,1);"); 893 // set cache size 894 int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE; 895 mDatabase.setMaxSqlCacheSize(N); 896 897 // try reduce cachesize 898 try { 899 mDatabase.setMaxSqlCacheSize(1); 900 } catch (IllegalStateException e) { 901 assertTrue(e.getMessage().contains("cannot set cacheSize to a value less than")); 902 } 903 } 904 905 @SmallTest testOpenDatabaseLookasideConfig()906 public void testOpenDatabaseLookasideConfig() { 907 // First check that lookaside is active 908 verifyLookasideStats(false); 909 // Reopen test db with lookaside disabled 910 mDatabase.close(); 911 SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder() 912 .setLookasideConfig(0, 0).build(); 913 mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params); 914 verifyLookasideStats(true); 915 } 916 verifyLookasideStats(boolean expectDisabled)917 void verifyLookasideStats(boolean expectDisabled) { 918 boolean dbStatFound = false; 919 SQLiteDebug.PagerStats info = SQLiteDebug.getDatabaseInfo(); 920 for (SQLiteDebug.DbStats dbStat : info.dbStats) { 921 if (dbStat.dbName.endsWith(mDatabaseFile.getName()) && !dbStat.arePoolStats) { 922 dbStatFound = true; 923 Log.i(TAG, "Lookaside for " + dbStat.dbName + " " + dbStat.lookaside); 924 if (expectDisabled) { 925 assertTrue("lookaside slots count should be zero", dbStat.lookaside == 0); 926 } else { 927 assertTrue("lookaside slots count should be greater than zero", 928 dbStat.lookaside > 0); 929 } 930 } 931 } 932 assertTrue("No dbstat found for " + mDatabaseFile.getName(), dbStatFound); 933 } 934 935 @SmallTest testOpenParamsSetLookasideConfigValidation()936 public void testOpenParamsSetLookasideConfigValidation() { 937 try { 938 SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder() 939 .setLookasideConfig(-1, 0).build(); 940 fail("Negative slot size should be rejected"); 941 } catch (IllegalArgumentException expected) { 942 } 943 try { 944 SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder() 945 .setLookasideConfig(0, -10).build(); 946 fail("Negative slot count should be rejected"); 947 } catch (IllegalArgumentException expected) { 948 } 949 } 950 951 @LargeTest testDefaultDatabaseErrorHandler()952 public void testDefaultDatabaseErrorHandler() { 953 DefaultDatabaseErrorHandler errorHandler = new DefaultDatabaseErrorHandler(); 954 955 // close the database. and call corruption handler. 956 // it should delete the database file. 957 File dbfile = new File(mDatabase.getPath()); 958 mDatabase.close(); 959 assertFalse(mDatabase.isOpen()); 960 assertTrue(dbfile.exists()); 961 try { 962 errorHandler.onCorruption(mDatabase); 963 assertFalse(dbfile.exists()); 964 } catch (Exception e) { 965 fail("unexpected"); 966 } 967 968 // create an in-memory database. and corruption handler shouldn't try to delete it 969 SQLiteDatabase memoryDb = SQLiteDatabase.openOrCreateDatabase(":memory:", null); 970 assertNotNull(memoryDb); 971 memoryDb.close(); 972 assertFalse(memoryDb.isOpen()); 973 try { 974 errorHandler.onCorruption(memoryDb); 975 } catch (Exception e) { 976 fail("unexpected"); 977 } 978 979 // create a database, keep it open, call corruption handler. database file should be deleted 980 SQLiteDatabase dbObj = SQLiteDatabase.openOrCreateDatabase(mDatabase.getPath(), null); 981 assertTrue(dbfile.exists()); 982 assertNotNull(dbObj); 983 assertTrue(dbObj.isOpen()); 984 try { 985 errorHandler.onCorruption(dbObj); 986 assertFalse(dbfile.exists()); 987 } catch (Exception e) { 988 fail("unexpected"); 989 } 990 991 // create a database, attach 2 more databases to it 992 // attached database # 1: ":memory:" 993 // attached database # 2: mDatabase.getPath() + "1"; 994 // call corruption handler. database files including the one for attached database # 2 995 // should be deleted 996 String attachedDb1File = mDatabase.getPath() + "1"; 997 dbObj = SQLiteDatabase.openOrCreateDatabase(mDatabase.getPath(), null); 998 dbObj.execSQL("ATTACH DATABASE ':memory:' as memoryDb"); 999 dbObj.execSQL("ATTACH DATABASE '" + attachedDb1File + "' as attachedDb1"); 1000 assertTrue(dbfile.exists()); 1001 assertTrue(new File(attachedDb1File).exists()); 1002 assertNotNull(dbObj); 1003 assertTrue(dbObj.isOpen()); 1004 List<Pair<String, String>> attachedDbs = dbObj.getAttachedDbs(); 1005 try { 1006 errorHandler.onCorruption(dbObj); 1007 assertFalse(dbfile.exists()); 1008 assertFalse(new File(attachedDb1File).exists()); 1009 } catch (Exception e) { 1010 fail("unexpected"); 1011 } 1012 1013 // same as above, except this is a bit of stress testing. attach 5 database files 1014 // and make sure they are all removed. 1015 int N = 5; 1016 ArrayList<String> attachedDbFiles = new ArrayList<String>(N); 1017 for (int i = 0; i < N; i++) { 1018 attachedDbFiles.add(mDatabase.getPath() + i); 1019 } 1020 dbObj = SQLiteDatabase.openOrCreateDatabase(mDatabase.getPath(), null); 1021 dbObj.execSQL("ATTACH DATABASE ':memory:' as memoryDb"); 1022 for (int i = 0; i < N; i++) { 1023 dbObj.execSQL("ATTACH DATABASE '" + attachedDbFiles.get(i) + "' as attachedDb" + i); 1024 } 1025 assertTrue(dbfile.exists()); 1026 for (int i = 0; i < N; i++) { 1027 assertTrue(new File(attachedDbFiles.get(i)).exists()); 1028 } 1029 assertNotNull(dbObj); 1030 assertTrue(dbObj.isOpen()); 1031 attachedDbs = dbObj.getAttachedDbs(); 1032 try { 1033 errorHandler.onCorruption(dbObj); 1034 assertFalse(dbfile.exists()); 1035 for (int i = 0; i < N; i++) { 1036 assertFalse(new File(attachedDbFiles.get(i)).exists()); 1037 } 1038 } catch (Exception e) { 1039 fail("unexpected"); 1040 } 1041 } 1042 1043 @MediumTest testCloseIdleConnection()1044 public void testCloseIdleConnection() throws Exception { 1045 mDatabase.close(); 1046 SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder() 1047 .setIdleConnectionTimeout(1000).build(); 1048 mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params); 1049 // Wait a bit and check that connection is still open 1050 Thread.sleep(100); 1051 String output = executeShellCommand("dumpsys dbinfo " + getContext().getPackageName()); 1052 assertTrue("Connection #0 should be open. Output: " + output, 1053 output.contains("Connection #0:")); 1054 1055 // Now cause idle timeout and check that connection is closed 1056 Thread.sleep(1000); 1057 output = executeShellCommand("dumpsys dbinfo " + getContext().getPackageName()); 1058 assertFalse("Connection #0 should be closed. Output: " + output, 1059 output.contains("Connection #0:")); 1060 } 1061 1062 @SmallTest testSetIdleConnectionTimeoutValidation()1063 public void testSetIdleConnectionTimeoutValidation() throws Exception { 1064 try { 1065 new SQLiteDatabase.OpenParams.Builder().setIdleConnectionTimeout(-1).build(); 1066 fail("Negative timeout should be rejected"); 1067 } catch (IllegalArgumentException expected) { 1068 } 1069 } 1070 executeShellCommand(String cmd)1071 private String executeShellCommand(String cmd) throws Exception { 1072 return UiDevice.getInstance( 1073 InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); 1074 } 1075 1076 @SmallTest testSavepointRollbacks()1077 public void testSavepointRollbacks() { 1078 try (SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(":memory:", null)) { 1079 db.execSQL("drop table if exists data"); 1080 db.execSQL("create table if not exists data (id INTEGER PRIMARY KEY, val TEXT)"); 1081 db.execSQL("begin deferred transaction"); 1082 db.execSQL("insert into data (val) values('row 1')"); 1083 db.execSQL("savepoint foo"); 1084 db.execSQL("insert into data (val) values('row 2')"); 1085 db.execSQL("rollback to foo"); 1086 db.execSQL("commit transaction"); 1087 long rowCount = DatabaseUtils.longForQuery(db, "select count(*) from data", null); 1088 assertEquals(1, rowCount); 1089 } 1090 } 1091 } 1092