SQLiteCursorTest.java revision c2ce721fcf684189b7251a5ecbf386426490d68e
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.database.sqlite;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.database.Cursor;
22import android.test.AndroidTestCase;
23import android.test.suitebuilder.annotation.LargeTest;
24import android.test.suitebuilder.annotation.SmallTest;
25import android.util.Log;
26
27import java.io.File;
28import java.util.HashSet;
29import java.util.Set;
30
31public class SQLiteCursorTest extends AndroidTestCase {
32    private SQLiteDatabase mDatabase;
33    private File mDatabaseFile;
34    private static final String TABLE_NAME = "testCursor";
35    @Override
36    protected void setUp() throws Exception {
37        super.setUp();
38
39        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
40        mDatabaseFile = new File(dbDir, "sqlitecursor_test.db");
41        if (mDatabaseFile.exists()) {
42            mDatabaseFile.delete();
43        }
44        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
45        assertNotNull(mDatabase);
46        // create a test table
47        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
48    }
49
50    @Override
51    protected void tearDown() throws Exception {
52        mDatabase.close();
53        mDatabaseFile.delete();
54        super.tearDown();
55    }
56
57    @SmallTest
58    public void testQueryObjReassignment() {
59        mDatabase.enableWriteAheadLogging();
60        // have a few connections in the database connection pool
61        DatabaseConnectionPool pool = mDatabase.mConnectionPool;
62        pool.setMaxPoolSize(5);
63        SQLiteCursor cursor =
64                (SQLiteCursor) mDatabase.rawQuery("select * from " + TABLE_NAME, null);
65        assertNotNull(cursor);
66        // it should use a pooled database connection
67        SQLiteDatabase db = cursor.getDatabase();
68        assertTrue(db.mConnectionNum > 0);
69        assertFalse(mDatabase.equals(db));
70        assertEquals(mDatabase, db.mParentConnObj);
71        assertTrue(pool.getConnectionList().contains(db));
72        assertTrue(db.isOpen());
73        // do a requery. cursor should continue to use the above pooled connection
74        cursor.requery();
75        SQLiteDatabase dbAgain = cursor.getDatabase();
76        assertEquals(db, dbAgain);
77        // disable WAL so that the pooled connection held by the above cursor is closed
78        mDatabase.disableWriteAheadLogging();
79        assertFalse(db.isOpen());
80        assertNull(mDatabase.mConnectionPool);
81        // requery - which should make the cursor use mDatabase connection since the pooled
82        // connection is no longer available
83        cursor.requery();
84        SQLiteDatabase db1 = cursor.getDatabase();
85        assertTrue(db1.mConnectionNum == 0);
86        assertEquals(mDatabase, db1);
87        assertNull(mDatabase.mConnectionPool);
88        assertTrue(db1.isOpen());
89        assertFalse(mDatabase.equals(db));
90        // enable WAL and requery - this time a pooled connection should be used
91        mDatabase.enableWriteAheadLogging();
92        cursor.requery();
93        db = cursor.getDatabase();
94        assertTrue(db.mConnectionNum > 0);
95        assertFalse(mDatabase.equals(db));
96        assertEquals(mDatabase, db.mParentConnObj);
97        assertTrue(mDatabase.mConnectionPool.getConnectionList().contains(db));
98        assertTrue(db.isOpen());
99    }
100
101    /**
102     * this test could take a while to execute. so, designate it as LargetTest
103     */
104    @LargeTest
105    public void testFillWindow() {
106        // create schema
107        final String testTable = "testV";
108        mDatabase.beginTransaction();
109        mDatabase.execSQL("CREATE TABLE " + testTable + " (col1 int, desc text not null);");
110        mDatabase.setTransactionSuccessful();
111        mDatabase.endTransaction();
112
113        // populate the table with data
114        // create a big string that will almost fit a page but not quite.
115        // since sqlite wants to make sure each row is in a page, this string will allocate
116        // a new database page for each row.
117        StringBuilder buff = new StringBuilder();
118        for (int i = 0; i < 500; i++) {
119            buff.append(i % 10 + "");
120        }
121        ContentValues values = new ContentValues();
122        values.put("desc", buff.toString());
123
124        // insert more than 1MB of data in the table. this should ensure that the entire tabledata
125        // will need more than one CursorWindow
126        int N = 5000;
127        Set<Integer> rows = new HashSet<Integer>();
128        mDatabase.beginTransaction();
129        for (int j = 0; j < N; j++) {
130            values.put("col1", j);
131            mDatabase.insert(testTable, null, values);
132            rows.add(j); // store in a hashtable so we can verify the results from cursor later on
133        }
134        mDatabase.setTransactionSuccessful();
135        mDatabase.endTransaction();
136        assertEquals(N, rows.size());
137        Cursor c1 = mDatabase.rawQuery("select * from " + testTable, null);
138        assertEquals(N, c1.getCount());
139        c1.close();
140
141        // scroll through ALL data in the table using a cursor. should cause multiple calls to
142        // native_fill_window (and re-fills of the CursorWindow object)
143        Cursor c = mDatabase.query(testTable, new String[]{"col1", "desc"},
144                null, null, null, null, null);
145        int i = 0;
146        while (c.moveToNext()) {
147            int val = c.getInt(0);
148            assertTrue(rows.contains(val));
149            assertTrue(rows.remove(val));
150        }
151        // did I see all the rows in the table?
152        assertTrue(rows.isEmpty());
153
154        // change data and make sure the cursor picks up new data & count
155        rows = new HashSet<Integer>();
156        mDatabase.beginTransaction();
157        int M = N + 1000;
158        for (int j = 0; j < M; j++) {
159            rows.add(j);
160            if (j < N) {
161                continue;
162            }
163            values.put("col1", j);
164            mDatabase.insert(testTable, null, values);
165        }
166        mDatabase.setTransactionSuccessful();
167        mDatabase.endTransaction();
168        assertEquals(M, rows.size());
169        c.requery();
170        i = 0;
171        while (c.moveToNext()) {
172            int val = c.getInt(0);
173            assertTrue(rows.contains(val));
174            assertTrue(rows.remove(val));
175        }
176        // did I see all data from the modified table
177        assertTrue(rows.isEmpty());
178
179        // move cursor back to 1st row and scroll to about halfway in the result set
180        // and then delete 75% of data - and then do requery
181        c.moveToFirst();
182        int K = N / 2;
183        for (int p = 0; p < K && c.moveToNext(); p++) {
184            // nothing to do - just scrolling to about half-point in the resultset
185        }
186        mDatabase.beginTransaction();
187        mDatabase.delete(testTable, "col1 < ?", new String[]{ (3 * M / 4) + ""});
188        mDatabase.setTransactionSuccessful();
189        mDatabase.endTransaction();
190        c.requery();
191        assertEquals(M / 4, c.getCount());
192        while (c.moveToNext()) {
193            // just move the cursor to next row - to make sure it can go through the entire
194            // resultset without any problems
195        }
196        c.close();
197    }
198}
199