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