1 package org.robolectric.shadows; 2 3 import static android.database.sqlite.SQLiteDatabase.OPEN_READWRITE; 4 import static com.google.common.truth.Truth.assertThat; 5 import static java.nio.charset.StandardCharsets.UTF_8; 6 import static org.junit.Assert.assertEquals; 7 import static org.junit.Assert.fail; 8 9 import android.content.ContentValues; 10 import android.database.Cursor; 11 import android.database.sqlite.SQLiteDatabase; 12 import android.database.sqlite.SQLiteException; 13 import android.os.CancellationSignal; 14 import android.os.OperationCanceledException; 15 import androidx.test.core.app.ApplicationProvider; 16 import androidx.test.ext.junit.runners.AndroidJUnit4; 17 import java.io.File; 18 import java.util.ArrayList; 19 import java.util.List; 20 import java.util.concurrent.CountDownLatch; 21 import org.junit.After; 22 import org.junit.Before; 23 import org.junit.Test; 24 import org.junit.runner.RunWith; 25 26 @RunWith(AndroidJUnit4.class) 27 public class SQLiteDatabaseTest { 28 private SQLiteDatabase database; 29 private List<SQLiteDatabase> openDatabases = new ArrayList<>(); 30 private static final String ANY_VALID_SQL = "SELECT 1"; 31 private File databasePath; 32 33 @Before 34 public void setUp() throws Exception { 35 databasePath = ApplicationProvider.getApplicationContext().getDatabasePath("database.db"); 36 databasePath.getParentFile().mkdirs(); 37 38 database = openOrCreateDatabase(databasePath); 39 database.execSQL("CREATE TABLE table_name (\n" + 40 " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + 41 " first_column VARCHAR(255),\n" + 42 " second_column BINARY,\n" + 43 " name VARCHAR(255),\n" + 44 " big_int INTEGER\n" + 45 ");"); 46 47 database.execSQL("CREATE TABLE rawtable (\n" + 48 " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + 49 " first_column VARCHAR(255),\n" + 50 " second_column BINARY,\n" + 51 " name VARCHAR(255),\n" + 52 " big_int INTEGER\n" + 53 ");"); 54 55 database.execSQL("CREATE TABLE exectable (\n" + 56 " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + 57 " first_column VARCHAR(255),\n" + 58 " second_column BINARY,\n" + 59 " name VARCHAR(255),\n" + 60 " big_int INTEGER\n" + 61 ");"); 62 63 database.execSQL("CREATE TABLE blob_table (\n" + 64 " id INTEGER PRIMARY KEY,\n" + 65 " blob_col BLOB\n" + 66 ");"); 67 68 String stringColumnValue = "column_value"; 69 byte[] byteColumnValue = new byte[]{1, 2, 3}; 70 71 ContentValues values = new ContentValues(); 72 73 values.put("first_column", stringColumnValue); 74 values.put("second_column", byteColumnValue); 75 76 database.insert("rawtable", null, values); 77 //////////////////////////////////////////////// 78 String stringColumnValue2 = "column_value2"; 79 byte[] byteColumnValue2 = new byte[]{4, 5, 6}; 80 ContentValues values2 = new ContentValues(); 81 82 values2.put("first_column", stringColumnValue2); 83 values2.put("second_column", byteColumnValue2); 84 85 database.insert("rawtable", null, values2); 86 } 87 88 @After 89 public void tearDown() throws Exception { 90 for (SQLiteDatabase openDatabase : openDatabases) { 91 openDatabase.close(); 92 } 93 } 94 95 @Test 96 public void testInsertAndQuery() throws Exception { 97 String stringColumnValue = "column_value"; 98 byte[] byteColumnValue = new byte[]{1, 2, 3}; 99 100 ContentValues values = new ContentValues(); 101 102 values.put("first_column", stringColumnValue); 103 values.put("second_column", byteColumnValue); 104 105 database.insert("table_name", null, values); 106 107 Cursor cursor = database.query("table_name", new String[]{"second_column", "first_column"}, null, null, null, null, null); 108 109 assertThat(cursor.moveToFirst()).isTrue(); 110 111 byte[] byteValueFromDatabase = cursor.getBlob(0); 112 String stringValueFromDatabase = cursor.getString(1); 113 114 assertThat(stringValueFromDatabase).isEqualTo(stringColumnValue); 115 assertThat(byteValueFromDatabase).isEqualTo(byteColumnValue); 116 } 117 118 @Test 119 public void testInsertAndRawQuery() throws Exception { 120 String stringColumnValue = "column_value"; 121 byte[] byteColumnValue = new byte[]{1, 2, 3}; 122 123 ContentValues values = new ContentValues(); 124 125 values.put("first_column", stringColumnValue); 126 values.put("second_column", byteColumnValue); 127 128 database.insert("table_name", null, values); 129 130 Cursor cursor = database.rawQuery("select second_column, first_column from table_name", null); 131 132 assertThat(cursor.moveToFirst()).isTrue(); 133 134 byte[] byteValueFromDatabase = cursor.getBlob(0); 135 String stringValueFromDatabase = cursor.getString(1); 136 137 assertThat(stringValueFromDatabase).isEqualTo(stringColumnValue); 138 assertThat(byteValueFromDatabase).isEqualTo(byteColumnValue); 139 } 140 141 @Test(expected = android.database.SQLException.class) 142 public void testInsertOrThrowWithSQLException() { 143 ContentValues values = new ContentValues(); 144 values.put("id", 1); 145 146 database.insertOrThrow("table_name", null, values); 147 database.insertOrThrow("table_name", null, values); 148 } 149 150 @Test 151 public void testInsertOrThrow() { 152 String stringColumnValue = "column_value"; 153 byte[] byteColumnValue = new byte[]{1, 2, 3}; 154 ContentValues values = new ContentValues(); 155 values.put("first_column", stringColumnValue); 156 values.put("second_column", byteColumnValue); 157 database.insertOrThrow("table_name", null, values); 158 159 Cursor cursor = database.rawQuery("select second_column, first_column from table_name", null); 160 assertThat(cursor.moveToFirst()).isTrue(); 161 byte[] byteValueFromDatabase = cursor.getBlob(0); 162 String stringValueFromDatabase = cursor.getString(1); 163 assertThat(stringValueFromDatabase).isEqualTo(stringColumnValue); 164 assertThat(byteValueFromDatabase).isEqualTo(byteColumnValue); 165 } 166 167 @Test(expected = IllegalArgumentException.class) 168 public void testRawQueryThrowsIndex0NullException() throws Exception { 169 database.rawQuery("select second_column, first_column from rawtable WHERE `id` = ?", new String[]{null}); 170 } 171 172 @Test(expected = IllegalArgumentException.class) 173 public void testRawQueryThrowsIndex0NullException2() throws Exception { 174 database.rawQuery("select second_column, first_column from rawtable", new String[]{null}); 175 } 176 177 @Test 178 public void testRawQueryCountWithOneArgument() throws Exception { 179 Cursor cursor = database.rawQuery("select second_column, first_column from rawtable WHERE `id` = ?", new String[]{"1"}); 180 assertThat(cursor.getCount()).isEqualTo(1); 181 } 182 183 @Test 184 public void testRawQueryCountWithNullArgs() throws Exception { 185 Cursor cursor = database.rawQuery("select second_column, first_column from rawtable", null); 186 assertThat(cursor.getCount()).isEqualTo(2); 187 } 188 189 @Test 190 public void testRawQueryCountWithEmptyArguments() throws Exception { 191 Cursor cursor = database.rawQuery("select second_column, first_column from rawtable", new String[]{}); 192 assertThat(cursor.getCount()).isEqualTo(2); 193 } 194 195 @Test(expected = IllegalArgumentException.class) 196 public void shouldThrowWhenArgumentsDoNotMatchQuery() throws Exception { 197 database.rawQuery("select second_column, first_column from rawtable", new String[]{"1"}); 198 } 199 200 @Test 201 public void testInsertWithException() { 202 ContentValues values = new ContentValues(); 203 204 assertEquals(-1, database.insert("table_that_doesnt_exist", null, values)); 205 } 206 207 208 @Test 209 public void testEmptyTable() throws Exception { 210 Cursor cursor = database.query("table_name", new String[]{"second_column", "first_column"}, null, null, null, null, null); 211 212 assertThat(cursor.moveToFirst()).isFalse(); 213 } 214 215 @Test 216 public void testInsertRowIdGeneration() throws Exception { 217 ContentValues values = new ContentValues(); 218 values.put("name", "Chuck"); 219 220 long id = database.insert("table_name", null, values); 221 222 assertThat(id).isNotEqualTo(0L); 223 } 224 225 @Test 226 public void testInsertKeyGeneration() throws Exception { 227 ContentValues values = new ContentValues(); 228 values.put("name", "Chuck"); 229 230 long key = database.insertWithOnConflict("table_name", null, values, SQLiteDatabase.CONFLICT_IGNORE); 231 232 assertThat(key).isNotEqualTo(0L); 233 } 234 235 @Test 236 public void testInsertEmptyBlobArgument() throws Exception { 237 ContentValues emptyBlobValues = new ContentValues(); 238 emptyBlobValues.put("id", 1); 239 emptyBlobValues.put("blob_col", new byte[]{}); 240 241 ContentValues nullBlobValues = new ContentValues(); 242 nullBlobValues.put("id", 2); 243 nullBlobValues.put("blob_col", (byte[])null); 244 245 long key = database.insertWithOnConflict("blob_table", null, emptyBlobValues, SQLiteDatabase.CONFLICT_FAIL); 246 assertThat(key).isNotEqualTo(0L); 247 key = database.insertWithOnConflict("blob_table", null, nullBlobValues, SQLiteDatabase.CONFLICT_FAIL); 248 assertThat(key).isNotEqualTo(0L); 249 250 Cursor cursor = database.query("blob_table", new String[]{"blob_col"}, "id=1", null, null, null, null); 251 try { 252 assertThat(cursor.moveToFirst()).isTrue(); 253 assertThat(cursor.getBlob(cursor.getColumnIndexOrThrow("blob_col"))).isNotNull(); 254 } finally { 255 cursor.close(); 256 } 257 258 cursor = database.query("blob_table", new String[]{"blob_col"}, "id=2", null, null, null, null); 259 try { 260 assertThat(cursor.moveToFirst()).isTrue(); 261 assertThat(cursor.getBlob(cursor.getColumnIndexOrThrow("blob_col"))).isNull(); 262 } finally { 263 cursor.close(); 264 } 265 266 } 267 268 @Test 269 public void testUpdate() throws Exception { 270 addChuck(); 271 272 assertThat(updateName(1234L, "Buster")).isEqualTo(1); 273 274 Cursor cursor = database.query("table_name", new String[]{"id", "name"}, null, null, null, null, null); 275 assertThat(cursor.moveToFirst()).isTrue(); 276 assertThat(cursor.getCount()).isEqualTo(1); 277 278 assertIdAndName(cursor, 1234L, "Buster"); 279 } 280 281 @Test 282 public void testUpdateNoMatch() throws Exception { 283 addChuck(); 284 285 assertThat(updateName(5678L, "Buster")).isEqualTo(0); 286 287 Cursor cursor = database.query("table_name", new String[]{"id", "name"}, null, null, null, null, null); 288 assertThat(cursor.moveToFirst()).isTrue(); 289 assertThat(cursor.getCount()).isEqualTo(1); 290 291 assertIdAndName(cursor, 1234L, "Chuck"); 292 } 293 294 @Test 295 public void testUpdateAll() throws Exception { 296 addChuck(); 297 addJulie(); 298 299 assertThat(updateName("Belvedere")).isEqualTo(2); 300 301 Cursor cursor = database.query("table_name", new String[]{"id", "name"}, null, null, null, null, null); 302 assertThat(cursor.moveToFirst()).isTrue(); 303 assertThat(cursor.getCount()).isEqualTo(2); 304 305 assertIdAndName(cursor, 1234L, "Belvedere"); 306 assertThat(cursor.moveToNext()).isTrue(); 307 308 assertIdAndName(cursor, 1235L, "Belvedere"); 309 assertThat(cursor.isLast()).isTrue(); 310 assertThat(cursor.moveToNext()).isFalse(); 311 assertThat(cursor.isAfterLast()).isTrue(); 312 assertThat(cursor.moveToNext()).isFalse(); 313 } 314 315 @Test 316 public void testDelete() throws Exception { 317 addChuck(); 318 319 int deleted = database.delete("table_name", "id=1234", null); 320 assertThat(deleted).isEqualTo(1); 321 322 assertEmptyDatabase(); 323 } 324 325 @Test 326 public void testDeleteNoMatch() throws Exception { 327 addChuck(); 328 329 int deleted = database.delete("table_name", "id=5678", null); 330 assertThat(deleted).isEqualTo(0); 331 332 assertNonEmptyDatabase(); 333 } 334 335 @Test 336 public void testDeleteAll() throws Exception { 337 addChuck(); 338 addJulie(); 339 340 int deleted = database.delete("table_name", "1", null); 341 assertThat(deleted).isEqualTo(2); 342 343 assertEmptyDatabase(); 344 } 345 346 @Test 347 public void testExecSQL() throws Exception { 348 database.execSQL("INSERT INTO table_name (id, name) VALUES(1234, 'Chuck');"); 349 350 Cursor cursor = database.rawQuery("SELECT COUNT(*) FROM table_name", null); 351 assertThat(cursor).isNotNull(); 352 assertThat(cursor.moveToNext()).isTrue(); 353 assertThat(cursor.getInt(0)).isEqualTo(1); 354 355 cursor = database.rawQuery("SELECT * FROM table_name", null); 356 assertThat(cursor).isNotNull(); 357 assertThat(cursor.moveToNext()).isTrue(); 358 359 assertThat(cursor.getInt(cursor.getColumnIndex("id"))).isEqualTo(1234); 360 assertThat(cursor.getString(cursor.getColumnIndex("name"))).isEqualTo("Chuck"); 361 } 362 363 @Test 364 public void testExecSQLParams() throws Exception { 365 database.execSQL("CREATE TABLE `routine` (`id` INTEGER PRIMARY KEY AUTOINCREMENT , `name` VARCHAR , `lastUsed` INTEGER DEFAULT 0 , UNIQUE (`name`)) ", new Object[]{}); 366 database.execSQL("INSERT INTO `routine` (`name` ,`lastUsed` ) VALUES (?,?)", new Object[]{"Leg Press", 0}); 367 database.execSQL("INSERT INTO `routine` (`name` ,`lastUsed` ) VALUES (?,?)", new Object[]{"Bench Press", 1}); 368 369 Cursor cursor = database.rawQuery("SELECT COUNT(*) FROM `routine`", null); 370 assertThat(cursor).isNotNull(); 371 assertThat(cursor.moveToNext()).isTrue(); 372 assertThat(cursor.getInt(0)).isEqualTo(2); 373 374 cursor = database.rawQuery("SELECT `id`, `name` ,`lastUsed` FROM `routine`", null); 375 assertThat(cursor).isNotNull(); 376 assertThat(cursor.moveToNext()).isTrue(); 377 378 assertThat(cursor.getInt(cursor.getColumnIndex("id"))).isEqualTo(1); 379 assertThat(cursor.getInt(cursor.getColumnIndex("lastUsed"))).isEqualTo(0); 380 assertThat(cursor.getString(cursor.getColumnIndex("name"))).isEqualTo("Leg Press"); 381 382 assertThat(cursor.moveToNext()).isTrue(); 383 384 assertThat(cursor.getInt(cursor.getColumnIndex("id"))).isEqualTo(2); 385 assertThat(cursor.getInt(cursor.getColumnIndex("lastUsed"))).isEqualTo(1); 386 assertThat(cursor.getString(cursor.getColumnIndex("name"))).isEqualTo("Bench Press"); 387 } 388 389 @Test(expected = SQLiteException.class) 390 public void execSqlShouldThrowOnBadQuery() throws Exception { 391 database.execSQL("INSERT INTO table_name;"); // invalid SQL 392 } 393 394 @Test(expected = IllegalArgumentException.class) 395 public void testExecSQLExceptionParametersWithoutArguments() throws Exception { 396 database.execSQL("insert into exectable (first_column) values (?);", null); 397 } 398 399 @Test(expected = IllegalArgumentException.class) 400 public void testExecSQLWithNullBindArgs() throws Exception { 401 database.execSQL("insert into exectable (first_column) values ('sdfsfs');", null); 402 } 403 404 @Test(expected = IllegalArgumentException.class) 405 public void testExecSQLTooManyBindArguments() throws Exception { 406 database.execSQL("insert into exectable (first_column) values ('kjhk');", new String[]{"xxxx"}); 407 } 408 409 @Test 410 public void testExecSQLWithEmptyBindArgs() throws Exception { 411 database.execSQL("insert into exectable (first_column) values ('eff');", new String[]{}); 412 } 413 414 @Test 415 public void testExecSQLInsertNull() throws Exception { 416 String name = "nullone"; 417 418 database.execSQL("insert into exectable (first_column, name) values (?,?);", new String[]{null, name}); 419 420 Cursor cursor = database.rawQuery("select * from exectable WHERE `name` = ?", new String[]{name}); 421 cursor.moveToFirst(); 422 int firstIndex = cursor.getColumnIndex("first_column"); 423 int nameIndex = cursor.getColumnIndex("name"); 424 assertThat(cursor.getString(nameIndex)).isEqualTo(name); 425 assertThat(cursor.getString(firstIndex)).isEqualTo(null); 426 427 } 428 429 @Test 430 public void testExecSQLAutoIncrementSQLite() throws Exception { 431 database.execSQL("CREATE TABLE auto_table (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255));"); 432 433 ContentValues values = new ContentValues(); 434 values.put("name", "Chuck"); 435 436 long key = database.insert("auto_table", null, values); 437 assertThat(key).isNotEqualTo(0L); 438 439 long key2 = database.insert("auto_table", null, values); 440 assertThat(key2).isNotEqualTo(key); 441 } 442 443 @Test(expected = IllegalStateException.class) 444 public void testClose() throws Exception { 445 database.close(); 446 447 database.execSQL("INSERT INTO table_name (id, name) VALUES(1234, 'Chuck');"); 448 } 449 450 @Test 451 public void testIsOpen() throws Exception { 452 assertThat(database.isOpen()).isTrue(); 453 database.close(); 454 assertThat(database.isOpen()).isFalse(); 455 } 456 457 @Test 458 public void shouldStoreGreatBigHonkingIntegersCorrectly() throws Exception { 459 database.execSQL("INSERT INTO table_name(big_int) VALUES(1234567890123456789);"); 460 Cursor cursor = database.query("table_name", new String[]{"big_int"}, null, null, null, null, null); 461 assertThat(cursor.moveToFirst()).isTrue(); 462 assertEquals(1234567890123456789L, cursor.getLong(0)); 463 } 464 465 @Test 466 public void testSuccessTransaction() throws Exception { 467 database.beginTransaction(); 468 database.execSQL("INSERT INTO table_name (id, name) VALUES(1234, 'Chuck');"); 469 database.setTransactionSuccessful(); 470 database.endTransaction(); 471 472 Cursor cursor = database.rawQuery("SELECT COUNT(*) FROM table_name", null); 473 assertThat(cursor.moveToNext()).isTrue(); 474 assertThat(cursor.getInt(0)).isEqualTo(1); 475 } 476 477 @Test 478 public void testFailureTransaction() throws Exception { 479 database.beginTransaction(); 480 481 database.execSQL("INSERT INTO table_name (id, name) VALUES(1234, 'Chuck');"); 482 483 final String select = "SELECT COUNT(*) FROM table_name"; 484 485 Cursor cursor = database.rawQuery(select, null); 486 assertThat(cursor.moveToNext()).isTrue(); 487 assertThat(cursor.getInt(0)).isEqualTo(1); 488 cursor.close(); 489 490 database.endTransaction(); 491 492 cursor = database.rawQuery(select, null); 493 assertThat(cursor.moveToNext()).isTrue(); 494 assertThat(cursor.getInt(0)).isEqualTo(0); 495 } 496 497 @Test 498 public void testSuccessNestedTransaction() throws Exception { 499 database.beginTransaction(); 500 database.execSQL("INSERT INTO table_name (id, name) VALUES(1234, 'Chuck');"); 501 database.beginTransaction(); 502 database.execSQL("INSERT INTO table_name (id, name) VALUES(12345, 'Julie');"); 503 database.setTransactionSuccessful(); 504 database.endTransaction(); 505 database.setTransactionSuccessful(); 506 database.endTransaction(); 507 508 Cursor cursor = database.rawQuery("SELECT COUNT(*) FROM table_name", null); 509 assertThat(cursor.moveToNext()).isTrue(); 510 assertThat(cursor.getInt(0)).isEqualTo(2); 511 } 512 513 @Test 514 public void testFailureNestedTransaction() throws Exception { 515 database.beginTransaction(); 516 database.execSQL("INSERT INTO table_name (id, name) VALUES(1234, 'Chuck');"); 517 database.beginTransaction(); 518 database.execSQL("INSERT INTO table_name (id, name) VALUES(12345, 'Julie');"); 519 database.endTransaction(); 520 database.setTransactionSuccessful(); 521 database.endTransaction(); 522 523 Cursor cursor = database.rawQuery("SELECT COUNT(*) FROM table_name", null); 524 assertThat(cursor.moveToNext()).isTrue(); 525 assertThat(cursor.getInt(0)).isEqualTo(0); 526 } 527 528 @Test 529 public void testTransactionAlreadySuccessful() { 530 database.beginTransaction(); 531 database.setTransactionSuccessful(); 532 try { 533 database.setTransactionSuccessful(); 534 fail("didn't receive the expected IllegalStateException"); 535 } catch (IllegalStateException e) { 536 assertThat(e.getMessage()).contains("transaction"); 537 assertThat(e.getMessage()).contains("successful"); 538 } 539 } 540 541 @Test 542 public void testInTransaction() throws Exception { 543 assertThat(database.inTransaction()).isFalse(); 544 database.beginTransaction(); 545 assertThat(database.inTransaction()).isTrue(); 546 database.endTransaction(); 547 assertThat(database.inTransaction()).isFalse(); 548 } 549 550 @Test 551 public void testReplace() throws Exception { 552 long id = addChuck(); 553 assertThat(id).isNotEqualTo(-1L); 554 555 ContentValues values = new ContentValues(); 556 values.put("id", id); 557 values.put("name", "Norris"); 558 559 long replaceId = database.replace("table_name", null, values); 560 assertThat(replaceId).isEqualTo(id); 561 562 String query = "SELECT name FROM table_name where id = " + id; 563 Cursor cursor = executeQuery(query); 564 565 assertThat(cursor.moveToNext()).isTrue(); 566 assertThat(cursor.getString(cursor.getColumnIndex("name"))).isEqualTo("Norris"); 567 } 568 569 @Test 570 public void testReplaceIsReplacing() throws Exception { 571 final String query = "SELECT first_column FROM table_name WHERE id = "; 572 String stringValueA = "column_valueA"; 573 String stringValueB = "column_valueB"; 574 long id = 1; 575 576 ContentValues valuesA = new ContentValues(); 577 valuesA.put("id", id); 578 valuesA.put("first_column", stringValueA); 579 580 ContentValues valuesB = new ContentValues(); 581 valuesB.put("id", id); 582 valuesB.put("first_column", stringValueB); 583 584 long firstId = database.replaceOrThrow("table_name", null, valuesA); 585 Cursor firstCursor = executeQuery(query + firstId); 586 assertThat(firstCursor.moveToNext()).isTrue(); 587 long secondId = database.replaceOrThrow("table_name", null, valuesB); 588 Cursor secondCursor = executeQuery(query + secondId); 589 assertThat(secondCursor.moveToNext()).isTrue(); 590 591 assertThat(firstId).isEqualTo(id); 592 assertThat(secondId).isEqualTo(id); 593 assertThat(firstCursor.getString(0)).isEqualTo(stringValueA); 594 assertThat(secondCursor.getString(0)).isEqualTo(stringValueB); 595 } 596 597 @Test 598 public void shouldCreateDefaultCursorFactoryWhenNullFactoryPassedToRawQuery() throws Exception { 599 database.rawQueryWithFactory(null, ANY_VALID_SQL, null, null); 600 } 601 602 @Test 603 public void shouldCreateDefaultCursorFactoryWhenNullFactoryPassedToQuery() throws Exception { 604 database.queryWithFactory(null, false, "table_name", null, null, null, null, null, null, null); 605 } 606 607 @Test 608 public void shouldOpenExistingDatabaseFromFileSystemIfFileExists() throws Exception { 609 610 database.close(); 611 612 SQLiteDatabase db = SQLiteDatabase.openDatabase(databasePath.getAbsolutePath(), null, OPEN_READWRITE); 613 Cursor c = db.rawQuery("select * from rawtable", null); 614 assertThat(c).isNotNull(); 615 assertThat(c.getCount()).isEqualTo(2); 616 assertThat(db.isOpen()).isTrue(); 617 db.close(); 618 assertThat(db.isOpen()).isFalse(); 619 620 SQLiteDatabase reopened = SQLiteDatabase.openDatabase(databasePath.getAbsolutePath(), null, OPEN_READWRITE); 621 assertThat(reopened).isNotSameAs(db); 622 assertThat(reopened.isOpen()).isTrue(); 623 } 624 625 @Test(expected = SQLiteException.class) 626 public void shouldThrowIfFileDoesNotExist() throws Exception { 627 File testDb = new File("/i/do/not/exist"); 628 assertThat(testDb.exists()).isFalse(); 629 SQLiteDatabase.openOrCreateDatabase(testDb.getAbsolutePath(), null); 630 } 631 632 @Test 633 public void shouldUseInMemoryDatabaseWhenCallingCreate() throws Exception { 634 SQLiteDatabase db = SQLiteDatabase.create(null); 635 assertThat(db.isOpen()).isTrue(); 636 assertThat(db.getPath()).isEqualTo(":memory:"); 637 } 638 639 @Test 640 public void shouldSetAndGetVersion() throws Exception { 641 assertThat(database.getVersion()).isEqualTo(0); 642 database.setVersion(20); 643 assertThat(database.getVersion()).isEqualTo(20); 644 } 645 646 @Test 647 public void testTwoConcurrentDbConnections() throws Exception { 648 SQLiteDatabase db1 = openOrCreateDatabase("db1"); 649 SQLiteDatabase db2 = openOrCreateDatabase("db2"); 650 651 db1.execSQL("CREATE TABLE foo(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT);"); 652 db2.execSQL("CREATE TABLE bar(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT);"); 653 654 ContentValues d1 = new ContentValues(); 655 d1.put("data", "d1"); 656 657 ContentValues d2 = new ContentValues(); 658 d2.put("data", "d2"); 659 660 db1.insert("foo", null, d1); 661 db2.insert("bar", null, d2); 662 663 Cursor c = db1.rawQuery("select * from foo", null); 664 assertThat(c).isNotNull(); 665 assertThat(c.getCount()).isEqualTo(1); 666 assertThat(c.moveToNext()).isTrue(); 667 assertThat(c.getString(c.getColumnIndex("data"))).isEqualTo("d1"); 668 669 c = db2.rawQuery("select * from bar", null); 670 assertThat(c).isNotNull(); 671 assertThat(c.getCount()).isEqualTo(1); 672 assertThat(c.moveToNext()).isTrue(); 673 assertThat(c.getString(c.getColumnIndex("data"))).isEqualTo("d2"); 674 } 675 676 @Test(expected = SQLiteException.class) 677 public void testQueryThrowsSQLiteException() throws Exception { 678 SQLiteDatabase db1 = openOrCreateDatabase("db1"); 679 db1.query("FOO", null, null, null, null, null, null); 680 } 681 682 @Test(expected = SQLiteException.class) 683 public void testShouldThrowSQLiteExceptionIfOpeningNonexistentDatabase() { 684 SQLiteDatabase.openDatabase("/does/not/exist", null, OPEN_READWRITE); 685 } 686 687 @Test 688 public void testCreateAndDropTable() throws Exception { 689 SQLiteDatabase db = openOrCreateDatabase("db1"); 690 db.execSQL("CREATE TABLE foo(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT);"); 691 Cursor c = db.query("FOO", null, null, null, null, null, null); 692 assertThat(c).isNotNull(); 693 c.close(); 694 db.close(); 695 db = openOrCreateDatabase("db1"); 696 db.execSQL("DROP TABLE IF EXISTS foo;"); 697 try { 698 c = db.query("FOO", null, null, null, null, null, null); 699 fail("expected no such table exception"); 700 } catch (SQLiteException e) { 701 // TODO 702 } 703 db.close(); 704 } 705 706 @Test 707 public void testCreateAndAlterTable() throws Exception { 708 SQLiteDatabase db = openOrCreateDatabase("db1"); 709 db.execSQL("CREATE TABLE foo(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT);"); 710 Cursor c = db.query("FOO", null, null, null, null, null, null); 711 assertThat(c).isNotNull(); 712 c.close(); 713 db.close(); 714 db = openOrCreateDatabase("db1"); 715 db.execSQL("ALTER TABLE foo ADD COLUMN more TEXT NULL;"); 716 c = db.query("FOO", null, null, null, null, null, null); 717 assertThat(c).isNotNull(); 718 int moreIndex = c.getColumnIndex("more"); 719 assertThat(moreIndex).isAtLeast(0); 720 c.close(); 721 } 722 723 @Test 724 public void testDataInMemoryDatabaseIsPersistentAfterClose() throws Exception { 725 SQLiteDatabase db1 = openOrCreateDatabase("db1"); 726 db1.execSQL("CREATE TABLE foo(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT);"); 727 ContentValues d1 = new ContentValues(); 728 d1.put("data", "d1"); 729 db1.insert("foo", null, d1); 730 db1.close(); 731 732 SQLiteDatabase db2 = openOrCreateDatabase("db1"); 733 Cursor c = db2.rawQuery("select * from foo", null); 734 assertThat(c).isNotNull(); 735 assertThat(c.getCount()).isEqualTo(1); 736 assertThat(c.moveToNext()).isTrue(); 737 assertThat(c.getString(c.getColumnIndex("data"))).isEqualTo("d1"); 738 } 739 740 @Test 741 public void testRawQueryWithFactoryAndCancellationSignal() throws Exception { 742 CancellationSignal signal = new CancellationSignal(); 743 744 Cursor cursor = database.rawQueryWithFactory(null, "select * from table_name", null, null, signal); 745 assertThat(cursor).isNotNull(); 746 assertThat(cursor.getColumnCount()).isEqualTo(5); 747 assertThat(cursor.isClosed()).isFalse(); 748 749 signal.cancel(); 750 751 try { 752 cursor.moveToNext(); 753 fail("did not get cancellation signal"); 754 } catch (OperationCanceledException e) { 755 // expected 756 } 757 } 758 759 @Test 760 public void shouldThrowWhenForeignKeysConstraintIsViolated() { 761 database.execSQL("CREATE TABLE master (master_value INTEGER)"); 762 database.execSQL("CREATE TABLE slave (master_value INTEGER REFERENCES master(master_value))"); 763 database.execSQL("PRAGMA foreign_keys=ON"); 764 try { 765 database.execSQL("INSERT INTO slave(master_value) VALUES (1)"); 766 fail("Foreign key constraint is violated but exception is not thrown"); 767 } catch (SQLiteException e) { 768 assertThat(e.getCause()).hasMessageThat().contains("foreign"); 769 } 770 } 771 772 773 @Test 774 public void shouldBeAbleToBeUsedFromDifferentThread() { 775 final CountDownLatch sync = new CountDownLatch(1); 776 final Throwable[] error = {null}; 777 778 new Thread() { 779 @Override 780 public void run() { 781 try { 782 executeQuery("select * from table_name"); 783 } catch (Throwable e) { 784 e.printStackTrace(); 785 error[0] = e; 786 } finally { 787 sync.countDown(); 788 } 789 } 790 } 791 .start(); 792 793 try { 794 sync.await(); 795 } catch (InterruptedException e) { 796 throw new RuntimeException(e); 797 } 798 799 assertThat(error[0]).isNull(); 800 } 801 802 803 private Cursor executeQuery(String query) { 804 return database.rawQuery(query, null); 805 } 806 807 private long addChuck() { 808 return addPerson(1234L, "Chuck"); 809 } 810 811 private long addJulie() { 812 return addPerson(1235L, "Julie"); 813 } 814 815 private long addPerson(long id, String name) { 816 ContentValues values = new ContentValues(); 817 values.put("id", id); 818 values.put("name", name); 819 return database.insert("table_name", null, values); 820 } 821 822 private int updateName(long id, String name) { 823 ContentValues values = new ContentValues(); 824 values.put("name", name); 825 return database.update("table_name", values, "id=" + id, null); 826 } 827 828 private int updateName(String name) { 829 ContentValues values = new ContentValues(); 830 values.put("name", name); 831 return database.update("table_name", values, null, null); 832 } 833 834 private void assertIdAndName(Cursor cursor, long id, String name) { 835 long idValueFromDatabase; 836 String stringValueFromDatabase; 837 838 idValueFromDatabase = cursor.getLong(0); 839 stringValueFromDatabase = cursor.getString(1); 840 assertThat(idValueFromDatabase).isEqualTo(id); 841 assertThat(stringValueFromDatabase).isEqualTo(name); 842 } 843 844 private void assertEmptyDatabase() { 845 Cursor cursor = database.query("table_name", new String[]{"id", "name"}, null, null, null, null, null); 846 assertThat(cursor.moveToFirst()).isFalse(); 847 assertThat(cursor.isClosed()).isFalse(); 848 assertThat(cursor.getCount()).isEqualTo(0); 849 } 850 851 private void assertNonEmptyDatabase() { 852 Cursor cursor = database.query("table_name", new String[]{"id", "name"}, null, null, null, null, null); 853 assertThat(cursor.moveToFirst()).isTrue(); 854 assertThat(cursor.getCount()).isNotEqualTo(0); 855 } 856 857 @Test 858 public void shouldAlwaysReturnCorrectIdFromInsert() throws Exception { 859 database.execSQL("CREATE TABLE table_A (\n" + 860 " _id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + 861 " id INTEGER DEFAULT 0\n" + 862 ");"); 863 864 database.execSQL("CREATE VIRTUAL TABLE new_search USING fts3 (id);"); 865 866 database.execSQL("CREATE TRIGGER t1 AFTER INSERT ON table_A WHEN new.id=0 BEGIN UPDATE table_A SET id=-new._id WHERE _id=new._id AND id=0; END;"); 867 database.execSQL("CREATE TRIGGER t2 AFTER INSERT ON table_A BEGIN INSERT INTO new_search (id) VALUES (new._id); END;"); 868 database.execSQL("CREATE TRIGGER t3 BEFORE UPDATE ON table_A BEGIN DELETE FROM new_search WHERE id MATCH old._id; END;"); 869 database.execSQL("CREATE TRIGGER t4 AFTER UPDATE ON table_A BEGIN INSERT INTO new_search (id) VALUES (new._id); END;"); 870 871 long[] returnedIds = new long[]{ 872 database.insert("table_A", "id", new ContentValues()), 873 database.insert("table_A", "id", new ContentValues()) 874 }; 875 876 Cursor c = database.query("table_A", new String[]{"_id"}, null, null, null, null, null); 877 assertThat(c).isNotNull(); 878 879 long[] actualIds = new long[c.getCount()]; 880 for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) { 881 actualIds[c.getPosition()] = c.getLong(c.getColumnIndexOrThrow("_id")); 882 } 883 c.close(); 884 885 assertThat(returnedIds).isEqualTo(actualIds); 886 } 887 888 @Test 889 public void shouldCorrectlyReturnNullValues() { 890 database.execSQL("CREATE TABLE null_test (col_int INTEGER, col_text TEXT, col_real REAL, col_blob BLOB)"); 891 892 ContentValues data = new ContentValues(); 893 data.putNull("col_int"); 894 data.putNull("col_text"); 895 data.putNull("col_real"); 896 data.putNull("col_blob"); 897 assertThat(database.insert("null_test", null, data)).isAtLeast(0L); 898 899 Cursor nullValuesCursor = database.query("null_test", null, null, null, null, null, null); 900 nullValuesCursor.moveToFirst(); 901 final int colsCount = 4; 902 for (int i = 0; i < colsCount; i++) { 903 assertThat(nullValuesCursor.getType(i)).isEqualTo(Cursor.FIELD_TYPE_NULL); 904 assertThat(nullValuesCursor.getString(i)).isNull(); 905 } 906 assertThat(nullValuesCursor.getBlob(3)).isNull(); 907 } 908 909 @Test 910 public void shouldGetBlobFromString() { 911 ContentValues values = new ContentValues(); 912 values.put("first_column", "this is a string"); 913 database.insert("table_name", null, values); 914 915 Cursor data = database.query("table_name", new String[]{"first_column"}, null, null, null, null, null); 916 assertThat(data.getCount()).isEqualTo(1); 917 data.moveToFirst(); 918 assertThat(data.getBlob(0)).isEqualTo(values.getAsString("first_column").getBytes(UTF_8)); 919 } 920 921 ///////////////////// 922 923 private SQLiteDatabase openOrCreateDatabase(String name) { 924 return openOrCreateDatabase(ApplicationProvider.getApplicationContext().getDatabasePath(name)); 925 } 926 927 private SQLiteDatabase openOrCreateDatabase(File databasePath) { 928 SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databasePath, null); 929 openDatabases.add(database); 930 return database; 931 } 932 } 933