| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.database.cts; |
| |
| import dalvik.annotation.BrokenTest; |
| |
| import java.io.File; |
| import java.util.Arrays; |
| import java.util.Random; |
| |
| import junit.framework.TestCase; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.database.CursorIndexOutOfBoundsException; |
| import android.database.CursorWrapper; |
| import android.database.DataSetObserver; |
| import android.database.DatabaseUtils; |
| import android.database.sqlite.SQLiteCursor; |
| import android.database.sqlite.SQLiteCursorDriver; |
| import android.database.sqlite.SQLiteDatabase; |
| import android.database.sqlite.SQLiteQuery; |
| import android.database.sqlite.SQLiteStatement; |
| import android.os.Looper; |
| import android.test.AndroidTestCase; |
| import android.test.PerformanceTestCase; |
| import android.test.suitebuilder.annotation.LargeTest; |
| import android.test.suitebuilder.annotation.MediumTest; |
| import android.test.suitebuilder.annotation.Suppress; |
| import android.util.Log; |
| |
| public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTestCase { |
| private static final String sString1 = "this is a test"; |
| private static final String sString2 = "and yet another test"; |
| private static final String sString3 = "this string is a little longer, but still a test"; |
| |
| private static final int CURRENT_DATABASE_VERSION = 42; |
| private SQLiteDatabase mDatabase; |
| private File mDatabaseFile; |
| protected static final int TYPE_CURSOR = 0; |
| protected static final int TYPE_CURSORWRAPPER = 1; |
| private int mTestType = TYPE_CURSOR; |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE); |
| mDatabaseFile = new File(dbDir, "database_test.db"); |
| if (mDatabaseFile.exists()) { |
| mDatabaseFile.delete(); |
| } |
| mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null); |
| assertNotNull(mDatabase); |
| mDatabase.setVersion(CURRENT_DATABASE_VERSION); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| mDatabase.close(); |
| mDatabaseFile.delete(); |
| super.tearDown(); |
| } |
| |
| public void setupTestType(int testType) { |
| mTestType = testType; |
| } |
| |
| private Cursor getTestCursor(Cursor cursor) { |
| switch (mTestType) { |
| case TYPE_CURSORWRAPPER: |
| return new CursorWrapper(cursor); |
| case TYPE_CURSOR: |
| default: |
| return cursor; |
| } |
| } |
| |
| public boolean isPerformanceOnly() { |
| return false; |
| } |
| |
| // These test can only be run once. |
| public int startPerformance(Intermediates intermediates) { |
| return 1; |
| } |
| |
| private void populateDefaultTable() { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);"); |
| |
| mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');"); |
| mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');"); |
| mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');"); |
| } |
| |
| @MediumTest |
| public void testCursorUpdate() { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, d INTEGER, s INTEGER);"); |
| for (int i = 0; i < 20; i++) { |
| mDatabase.execSQL("INSERT INTO test (d, s) VALUES (" + i + "," + i % 2 + ");"); |
| } |
| |
| Cursor testCursor = getTestCursor(mDatabase.query("test", null, "s = 0", null, null, null, |
| null)); |
| |
| int dCol = testCursor.getColumnIndexOrThrow("d"); |
| int sCol = testCursor.getColumnIndexOrThrow("s"); |
| |
| int count = 0; |
| while (testCursor.moveToNext()) { |
| assertTrue(testCursor.updateInt(dCol, 3)); |
| count++; |
| } |
| assertEquals(10, count); |
| |
| assertTrue(testCursor.commitUpdates()); |
| |
| assertTrue(testCursor.requery()); |
| |
| count = 0; |
| while (testCursor.moveToNext()) { |
| assertEquals(3, testCursor.getInt(dCol)); |
| count++; |
| } |
| |
| assertEquals(10, count); |
| assertTrue(testCursor.moveToFirst()); |
| assertTrue(testCursor.deleteRow()); |
| assertEquals(9, testCursor.getCount()); |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testBlob() throws Exception { |
| // create table |
| mDatabase.execSQL( |
| "CREATE TABLE test (_id INTEGER PRIMARY KEY, s TEXT, d REAL, l INTEGER, b BLOB);"); |
| // insert blob |
| Object[] args = new Object[4]; |
| |
| byte[] blob = new byte[1000]; |
| byte value = 99; |
| Arrays.fill(blob, value); |
| args[3] = blob; |
| |
| String s = new String("text"); |
| args[0] = s; |
| Double d = 99.9; |
| args[1] = d; |
| Long l = (long) 1000; |
| args[2] = l; |
| |
| String sql = "INSERT INTO test (s, d, l, b) VALUES (?,?,?,?)"; |
| mDatabase.execSQL(sql, args); |
| // use cursor to access blob |
| |
| Cursor testCursor = mDatabase.query("test", null, null, null, null, null, null); |
| |
| testCursor.moveToNext(); |
| ContentValues cv = new ContentValues(); |
| DatabaseUtils.cursorRowToContentValues(testCursor, cv); |
| |
| int bCol = testCursor.getColumnIndexOrThrow("b"); |
| int sCol = testCursor.getColumnIndexOrThrow("s"); |
| int dCol = testCursor.getColumnIndexOrThrow("d"); |
| int lCol = testCursor.getColumnIndexOrThrow("l"); |
| byte[] cBlob = testCursor.getBlob(bCol); |
| assertTrue(Arrays.equals(blob, cBlob)); |
| assertEquals(s, testCursor.getString(sCol)); |
| assertEquals((double) d, testCursor.getDouble(dCol)); |
| assertEquals((long) l, testCursor.getLong(lCol)); |
| |
| // new byte[] |
| byte[] newblob = new byte[1000]; |
| value = 98; |
| Arrays.fill(blob, value); |
| |
| testCursor.updateBlob(bCol, newblob); |
| cBlob = testCursor.getBlob(bCol); |
| assertTrue(Arrays.equals(newblob, cBlob)); |
| |
| // commit |
| assertTrue(testCursor.commitUpdates()); |
| assertTrue(testCursor.requery()); |
| testCursor.moveToNext(); |
| cBlob = testCursor.getBlob(bCol); |
| assertTrue(Arrays.equals(newblob, cBlob)); |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testRealColumns() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data REAL);"); |
| ContentValues values = new ContentValues(); |
| values.put("data", 42.11); |
| long id = mDatabase.insert("test", "data", values); |
| assertTrue(id > 0); |
| Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT data FROM test", null)); |
| assertNotNull(testCursor); |
| assertTrue(testCursor.moveToFirst()); |
| assertEquals(42.11, testCursor.getDouble(0)); |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testCursor1() throws Exception { |
| populateDefaultTable(); |
| |
| Cursor testCursor = getTestCursor(mDatabase.query("test", null, null, null, null, null, |
| null)); |
| |
| int dataColumn = testCursor.getColumnIndexOrThrow("data"); |
| |
| // The cursor should ignore text before the last period when looking for a column. (This |
| // is a temporary hack in all implementations of getColumnIndex.) |
| int dataColumn2 = testCursor.getColumnIndexOrThrow("junk.data"); |
| assertEquals(dataColumn, dataColumn2); |
| |
| assertSame(3, testCursor.getCount()); |
| |
| assertTrue(testCursor.isBeforeFirst()); |
| |
| try { |
| testCursor.getInt(0); |
| fail("CursorIndexOutOfBoundsException expected"); |
| } catch (CursorIndexOutOfBoundsException ex) { |
| // expected |
| } |
| |
| testCursor.moveToNext(); |
| assertEquals(1, testCursor.getInt(0)); |
| |
| String s = testCursor.getString(dataColumn); |
| assertEquals(sString1, s); |
| |
| testCursor.moveToNext(); |
| s = testCursor.getString(dataColumn); |
| assertEquals(sString2, s); |
| |
| testCursor.moveToNext(); |
| s = testCursor.getString(dataColumn); |
| assertEquals(sString3, s); |
| |
| testCursor.moveToPosition(-1); |
| testCursor.moveToNext(); |
| s = testCursor.getString(dataColumn); |
| assertEquals(sString1, s); |
| |
| testCursor.moveToPosition(2); |
| s = testCursor.getString(dataColumn); |
| assertEquals(sString3, s); |
| |
| int i; |
| |
| for (testCursor.moveToFirst(), i = 0; !testCursor.isAfterLast(); |
| testCursor.moveToNext(), i++) { |
| testCursor.getInt(0); |
| } |
| |
| assertEquals(3, i); |
| |
| try { |
| testCursor.getInt(0); |
| fail("CursorIndexOutOfBoundsException expected"); |
| } catch (CursorIndexOutOfBoundsException ex) { |
| // expected |
| } |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testCursor2() throws Exception { |
| populateDefaultTable(); |
| |
| Cursor testCursor = getTestCursor(mDatabase.query("test", null, "_id > 1000", null, null, |
| null, null)); |
| assertEquals(0, testCursor.getCount()); |
| assertTrue(testCursor.isBeforeFirst()); |
| |
| try { |
| testCursor.getInt(0); |
| fail("CursorIndexOutOfBoundsException expected"); |
| } catch (CursorIndexOutOfBoundsException ex) { |
| // expected |
| } |
| |
| int i; |
| for (testCursor.moveToFirst(), i = 0; !testCursor.isAfterLast(); |
| testCursor.moveToNext(), i++) { |
| testCursor.getInt(0); |
| } |
| assertEquals(0, i); |
| try { |
| testCursor.getInt(0); |
| fail("CursorIndexOutOfBoundsException expected"); |
| } catch (CursorIndexOutOfBoundsException ex) { |
| // expected |
| } |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testLargeField() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);"); |
| |
| StringBuilder sql = new StringBuilder(2100); |
| sql.append("INSERT INTO test (data) VALUES ('"); |
| Random random = new Random(System.currentTimeMillis()); |
| StringBuilder randomString = new StringBuilder(1979); |
| for (int i = 0; i < 1979; i++) { |
| randomString.append((random.nextInt() & 0xf) % 10); |
| } |
| sql.append(randomString); |
| sql.append("');"); |
| mDatabase.execSQL(sql.toString()); |
| |
| Cursor testCursor = getTestCursor(mDatabase.query("test", null, null, null, null, null, |
| null)); |
| assertNotNull(testCursor); |
| assertEquals(1, testCursor.getCount()); |
| |
| assertTrue(testCursor.moveToFirst()); |
| assertEquals(0, testCursor.getPosition()); |
| String largeString = testCursor.getString(testCursor.getColumnIndexOrThrow("data")); |
| assertNotNull(largeString); |
| assertEquals(randomString.toString(), largeString); |
| testCursor.close(); |
| } |
| |
| private class TestObserver extends DataSetObserver { |
| int total; |
| SQLiteCursor c; |
| boolean quit = false; |
| |
| public TestObserver(int total_, SQLiteCursor cursor) { |
| c = cursor; |
| total = total_; |
| } |
| |
| @Override |
| public void onChanged() { |
| int count = c.getCount(); |
| if (total == count) { |
| int i = 0; |
| while (c.moveToNext()) { |
| assertEquals(i, c.getInt(1)); |
| i++; |
| } |
| assertEquals(count, i); |
| quit = true; |
| Looper.myLooper().quit(); |
| } |
| } |
| |
| @Override |
| public void onInvalidated() { |
| } |
| } |
| |
| //@Large |
| @Suppress |
| public void testLoadingThreadDelayRegisterData() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); |
| |
| final int count = 505; |
| String sql = "INSERT INTO test (data) VALUES (?);"; |
| SQLiteStatement s = mDatabase.compileStatement(sql); |
| for (int i = 0; i < count; i++) { |
| s.bindLong(1, i); |
| s.execute(); |
| } |
| |
| int maxRead = 500; |
| int initialRead = 5; |
| SQLiteCursor testCursor = (SQLiteCursor) mDatabase.rawQuery("select * from test;", null, |
| initialRead, maxRead); |
| |
| TestObserver observer = new TestObserver(count, testCursor); |
| testCursor.getCount(); |
| testCursor.registerDataSetObserver(observer); |
| if (!observer.quit) { |
| Looper.loop(); |
| } |
| testCursor.close(); |
| } |
| |
| @LargeTest |
| @BrokenTest("Consistently times out, even with count reduced to 30000") |
| public void testLoadingThread() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); |
| |
| final int count = 50000; |
| String sql = "INSERT INTO test (data) VALUES (?);"; |
| SQLiteStatement s = mDatabase.compileStatement(sql); |
| for (int i = 0; i < count; i++) { |
| s.bindLong(1, i); |
| s.execute(); |
| } |
| |
| int maxRead = 1000; |
| int initialRead = 5; |
| SQLiteCursor testCursor = (SQLiteCursor) mDatabase.rawQuery("select * from test;", null, |
| initialRead, maxRead); |
| |
| TestObserver observer = new TestObserver(count, testCursor); |
| testCursor.registerDataSetObserver(observer); |
| testCursor.getCount(); |
| |
| Looper.loop(); |
| testCursor.close(); |
| } |
| |
| @LargeTest |
| public void testLoadingThreadClose() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); |
| |
| final int count = 1000; |
| String sql = "INSERT INTO test (data) VALUES (?);"; |
| SQLiteStatement s = mDatabase.compileStatement(sql); |
| for (int i = 0; i < count; i++) { |
| s.bindLong(1, i); |
| s.execute(); |
| } |
| |
| int maxRead = 11; |
| int initialRead = 5; |
| SQLiteCursor testCursor = (SQLiteCursor) mDatabase.rawQuery("select * from test;", null, |
| initialRead, maxRead); |
| |
| TestObserver observer = new TestObserver(count, testCursor); |
| testCursor.registerDataSetObserver(observer); |
| testCursor.getCount(); |
| testCursor.close(); |
| } |
| |
| @LargeTest |
| public void testLoadingThreadDeactivate() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); |
| |
| final int count = 1000; |
| String sql = "INSERT INTO test (data) VALUES (?);"; |
| SQLiteStatement s = mDatabase.compileStatement(sql); |
| for (int i = 0; i < count; i++) { |
| s.bindLong(1, i); |
| s.execute(); |
| } |
| |
| int maxRead = 11; |
| int initialRead = 5; |
| SQLiteCursor testCursor = (SQLiteCursor) mDatabase.rawQuery("select * from test;", null, |
| initialRead, maxRead); |
| |
| TestObserver observer = new TestObserver(count, testCursor); |
| testCursor.registerDataSetObserver(observer); |
| testCursor.getCount(); |
| testCursor.deactivate(); |
| testCursor.close(); |
| } |
| |
| @LargeTest |
| public void testManyRowsLong() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); |
| |
| final int count = 9000; |
| for (int i = 0; i < count; i++) { |
| mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");"); |
| } |
| |
| Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "data" }, |
| null, null, null, null, null)); |
| assertNotNull(testCursor); |
| |
| int i = 0; |
| while (testCursor.moveToNext()) { |
| assertEquals(i, testCursor.getInt(0)); |
| i++; |
| } |
| assertEquals(count, i); |
| assertEquals(count, testCursor.getCount()); |
| |
| Log.d("testManyRows", "count " + Integer.toString(i)); |
| testCursor.close(); |
| } |
| |
| @LargeTest |
| public void testManyRowsTxt() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);"); |
| StringBuilder sql = new StringBuilder(2100); |
| sql.append("INSERT INTO test (data) VALUES ('"); |
| Random random = new Random(System.currentTimeMillis()); |
| StringBuilder randomString = new StringBuilder(1979); |
| for (int i = 0; i < 1979; i++) { |
| randomString.append((random.nextInt() & 0xf) % 10); |
| } |
| sql.append(randomString); |
| sql.append("');"); |
| |
| // if cursor window size changed, adjust this value too |
| final int count = 600; // more than two fillWindow needed |
| for (int i = 0; i < count; i++) { |
| mDatabase.execSQL(sql.toString()); |
| } |
| |
| Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "data" }, null, |
| null, null, null, null)); |
| assertNotNull(testCursor); |
| |
| int i = 0; |
| while (testCursor.moveToNext()) { |
| assertEquals(randomString.toString(), testCursor.getString(0)); |
| i++; |
| } |
| assertEquals(count, i); |
| assertEquals(count, testCursor.getCount()); |
| testCursor.close(); |
| } |
| |
| @LargeTest |
| public void testManyRowsTxtLong() throws Exception { |
| mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, txt TEXT, data INT);"); |
| |
| Random random = new Random(System.currentTimeMillis()); |
| StringBuilder randomString = new StringBuilder(1979); |
| for (int i = 0; i < 1979; i++) { |
| randomString.append((random.nextInt() & 0xf) % 10); |
| } |
| |
| // if cursor window size changed, adjust this value too |
| final int count = 600; |
| for (int i = 0; i < count; i++) { |
| StringBuilder sql = new StringBuilder(2100); |
| sql.append("INSERT INTO test (txt, data) VALUES ('"); |
| sql.append(randomString); |
| sql.append("','"); |
| sql.append(i); |
| sql.append("');"); |
| mDatabase.execSQL(sql.toString()); |
| } |
| |
| Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "txt", "data" }, |
| null, null, null, null, null)); |
| assertNotNull(testCursor); |
| |
| int i = 0; |
| while (testCursor.moveToNext()) { |
| assertEquals(randomString.toString(), testCursor.getString(0)); |
| assertEquals(i, testCursor.getInt(1)); |
| i++; |
| } |
| assertEquals(count, i); |
| assertEquals(count, testCursor.getCount()); |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testRequery() throws Exception { |
| populateDefaultTable(); |
| |
| Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT * FROM test", null)); |
| assertNotNull(testCursor); |
| assertEquals(3, testCursor.getCount()); |
| testCursor.deactivate(); |
| testCursor.requery(); |
| assertEquals(3, testCursor.getCount()); |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testRequeryWithSelection() throws Exception { |
| populateDefaultTable(); |
| |
| Cursor testCursor = getTestCursor( |
| mDatabase.rawQuery("SELECT data FROM test WHERE data = '" + sString1 + "'", |
| null)); |
| assertNotNull(testCursor); |
| assertEquals(1, testCursor.getCount()); |
| assertTrue(testCursor.moveToFirst()); |
| assertEquals(sString1, testCursor.getString(0)); |
| testCursor.deactivate(); |
| testCursor.requery(); |
| assertEquals(1, testCursor.getCount()); |
| assertTrue(testCursor.moveToFirst()); |
| assertEquals(sString1, testCursor.getString(0)); |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testRequeryWithSelectionArgs() throws Exception { |
| populateDefaultTable(); |
| |
| Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT data FROM test WHERE data = ?", |
| new String[] { sString1 })); |
| assertNotNull(testCursor); |
| assertEquals(1, testCursor.getCount()); |
| assertTrue(testCursor.moveToFirst()); |
| assertEquals(sString1, testCursor.getString(0)); |
| testCursor.deactivate(); |
| testCursor.requery(); |
| assertEquals(1, testCursor.getCount()); |
| assertTrue(testCursor.moveToFirst()); |
| assertEquals(sString1, testCursor.getString(0)); |
| testCursor.close(); |
| } |
| |
| @MediumTest |
| public void testRequeryWithAlteredSelectionArgs() throws Exception { |
| /** |
| * Test the ability of a subclass of SQLiteCursor to change its query arguments. |
| */ |
| populateDefaultTable(); |
| |
| SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() { |
| public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, |
| String editTable, SQLiteQuery query) { |
| return new SQLiteCursor(db, masterQuery, editTable, query) { |
| @Override |
| public boolean requery() { |
| setSelectionArguments(new String[] { "2" }); |
| return super.requery(); |
| } |
| }; |
| } |
| }; |
| Cursor testCursor = getTestCursor(mDatabase.rawQueryWithFactory(factory, |
| "SELECT data FROM test WHERE _id <= ?", |
| new String[] { "1" }, null)); |
| assertNotNull(testCursor); |
| assertEquals(1, testCursor.getCount()); |
| assertTrue(testCursor.moveToFirst()); |
| assertEquals(sString1, testCursor.getString(0)); |
| |
| // Our hacked requery() changes the query arguments in the cursor. |
| testCursor.requery(); |
| |
| assertEquals(2, testCursor.getCount()); |
| assertTrue(testCursor.moveToFirst()); |
| assertEquals(sString1, testCursor.getString(0)); |
| assertTrue(testCursor.moveToNext()); |
| assertEquals(sString2, testCursor.getString(0)); |
| |
| // Test that setting query args on a deactivated cursor also works. |
| testCursor.deactivate(); |
| testCursor.requery(); |
| } |
| } |