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