• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 
17 package android.database.sqlite.cts;
18 
19 
20 import android.content.Context;
21 import android.database.AbstractCursor;
22 import android.database.AbstractWindowedCursor;
23 import android.database.Cursor;
24 import android.database.CursorWindow;
25 import android.database.DataSetObserver;
26 import android.database.SQLException;
27 import android.database.StaleDataException;
28 import android.database.sqlite.SQLiteBlobTooBigException;
29 import android.database.sqlite.SQLiteCursor;
30 import android.database.sqlite.SQLiteDatabase;
31 import android.database.sqlite.SQLiteDirectCursorDriver;
32 import android.test.AndroidTestCase;
33 
34 import java.util.Arrays;
35 
36 /**
37  * Test {@link AbstractCursor}.
38  */
39 public class SQLiteCursorTest extends AndroidTestCase {
40     private SQLiteDatabase mDatabase;
41     private static final String[] COLUMNS = new String[] { "_id", "number_1", "number_2" };
42     private static final String TABLE_NAME = "test";
43     private static final String TABLE_COLUMNS = " number_1 INTEGER, number_2 INTEGER";
44     private static final int DEFAULT_TABLE_VALUE_BEGINS = 1;
45     private static final int TEST_COUNT = 10;
46     private static final String TEST_SQL = "SELECT * FROM test ORDER BY number_1";
47     private static final String DATABASE_FILE = "database_test.db";
48 
49     @Override
setUp()50     protected void setUp() throws Exception {
51         super.setUp();
52         getContext().deleteDatabase(DATABASE_FILE);
53         mDatabase = getContext().openOrCreateDatabase(DATABASE_FILE, Context.MODE_PRIVATE, null);
54         createTable(TABLE_NAME, TABLE_COLUMNS);
55         addValuesIntoTable(TABLE_NAME, DEFAULT_TABLE_VALUE_BEGINS, TEST_COUNT);
56     }
57 
58     @Override
tearDown()59     protected void tearDown() throws Exception {
60         mDatabase.close();
61         getContext().deleteDatabase(DATABASE_FILE);
62         super.tearDown();
63     }
64 
testConstructor()65     public void testConstructor() {
66         SQLiteDirectCursorDriver cursorDriver = new SQLiteDirectCursorDriver(mDatabase,
67                 TEST_SQL, TABLE_NAME, null);
68         try {
69             new SQLiteCursor(mDatabase, cursorDriver, TABLE_NAME, null);
70             fail("constructor didn't throw IllegalArgumentException when SQLiteQuery is null");
71         } catch (IllegalArgumentException e) {
72         }
73 
74         // get SQLiteCursor by querying database
75         SQLiteCursor cursor = getCursor();
76         assertNotNull(cursor);
77     }
78 
testClose()79     public void testClose() {
80         SQLiteCursor cursor = getCursor();
81         assertTrue(cursor.moveToFirst());
82         assertFalse(cursor.isClosed());
83         assertTrue(cursor.requery());
84         cursor.close();
85         assertFalse(cursor.requery());
86         try {
87             cursor.moveToFirst();
88             fail("moveToFirst didn't throw IllegalStateException after closed.");
89         } catch (IllegalStateException e) {
90         }
91         assertTrue(cursor.isClosed());
92     }
93 
testRegisterDataSetObserver()94     public void testRegisterDataSetObserver() {
95         SQLiteCursor cursor = getCursor();
96         MockCursorWindow cursorWindow = new MockCursorWindow(false);
97 
98         MockObserver observer = new MockObserver();
99 
100         cursor.setWindow(cursorWindow);
101         // Before registering, observer can't be notified.
102         assertFalse(observer.hasInvalidated());
103         cursor.moveToLast();
104         assertFalse(cursorWindow.isClosed());
105         cursor.deactivate();
106         assertFalse(observer.hasInvalidated());
107         // deactivate() will close the CursorWindow
108         assertTrue(cursorWindow.isClosed());
109 
110         // test registering DataSetObserver
111         assertTrue(cursor.requery());
112         cursor.registerDataSetObserver(observer);
113         assertFalse(observer.hasInvalidated());
114         cursor.moveToLast();
115         assertEquals(TEST_COUNT, cursor.getInt(1));
116         cursor.deactivate();
117         // deactivate method can invoke invalidate() method, can be observed by DataSetObserver.
118         assertTrue(observer.hasInvalidated());
119 
120         try {
121             cursor.getInt(1);
122             fail("After deactivating, cursor cannot execute getting value operations.");
123         } catch (StaleDataException e) {
124         }
125 
126         assertTrue(cursor.requery());
127         cursor.moveToLast();
128         assertEquals(TEST_COUNT, cursor.getInt(1));
129 
130         // can't register a same observer twice.
131         try {
132             cursor.registerDataSetObserver(observer);
133             fail("didn't throw IllegalStateException when register existed observer");
134         } catch (IllegalStateException e) {
135         }
136 
137         // after unregistering, observer can't be notified.
138         cursor.unregisterDataSetObserver(observer);
139         observer.resetStatus();
140         assertFalse(observer.hasInvalidated());
141         cursor.deactivate();
142         assertFalse(observer.hasInvalidated());
143     }
144 
testRequery()145     public void testRequery() {
146         final String DELETE = "DELETE FROM " + TABLE_NAME + " WHERE number_1 =";
147         final String DELETE_1 = DELETE + "1;";
148         final String DELETE_2 = DELETE + "2;";
149 
150         mDatabase.execSQL(DELETE_1);
151         // when cursor is created, it refreshes CursorWindow and populates cursor count
152         SQLiteCursor cursor = getCursor();
153         MockObserver observer = new MockObserver();
154         cursor.registerDataSetObserver(observer);
155         assertEquals(TEST_COUNT - 1, cursor.getCount());
156         assertFalse(observer.hasChanged());
157 
158         mDatabase.execSQL(DELETE_2);
159         // when getCount() has invoked once, it can no longer refresh CursorWindow.
160         assertEquals(TEST_COUNT - 1, cursor.getCount());
161 
162         assertTrue(cursor.requery());
163         // only after requery, getCount can get most up-to-date counting info now.
164         assertEquals(TEST_COUNT - 2, cursor.getCount());
165         assertTrue(observer.hasChanged());
166     }
167 
testRequery2()168     public void testRequery2() {
169         mDatabase.disableWriteAheadLogging();
170         mDatabase.execSQL("create table testRequery2 (i int);");
171         mDatabase.execSQL("insert into testRequery2 values(1);");
172         mDatabase.execSQL("insert into testRequery2 values(2);");
173         Cursor c = mDatabase.rawQuery("select * from testRequery2 order by i", null);
174         assertEquals(2, c.getCount());
175         assertTrue(c.moveToFirst());
176         assertEquals(1, c.getInt(0));
177         assertTrue(c.moveToNext());
178         assertEquals(2, c.getInt(0));
179         // add more data to the table and requery
180         mDatabase.execSQL("insert into testRequery2 values(3);");
181         assertTrue(c.requery());
182         assertEquals(3, c.getCount());
183         assertTrue(c.moveToFirst());
184         assertEquals(1, c.getInt(0));
185         assertTrue(c.moveToNext());
186         assertEquals(2, c.getInt(0));
187         assertTrue(c.moveToNext());
188         assertEquals(3, c.getInt(0));
189         // close the database and see if requery throws an exception
190         mDatabase.close();
191         assertFalse(c.requery());
192     }
193 
testGetColumnIndex()194     public void testGetColumnIndex() {
195         SQLiteCursor cursor = getCursor();
196 
197         for (int i = 0; i < COLUMNS.length; i++) {
198             assertEquals(i, cursor.getColumnIndex(COLUMNS[i]));
199         }
200 
201         assertTrue(Arrays.equals(COLUMNS, cursor.getColumnNames()));
202     }
203 
testSetSelectionArguments()204     public void testSetSelectionArguments() {
205         final String SELECTION = "_id > ?";
206         int TEST_ARG1 = 2;
207         int TEST_ARG2 = 5;
208         SQLiteCursor cursor = (SQLiteCursor) mDatabase.query(TABLE_NAME, null, SELECTION,
209                 new String[] { Integer.toString(TEST_ARG1) }, null, null, null);
210         assertEquals(TEST_COUNT - TEST_ARG1, cursor.getCount());
211         cursor.setSelectionArguments(new String[] { Integer.toString(TEST_ARG2) });
212         cursor.requery();
213         assertEquals(TEST_COUNT - TEST_ARG2, cursor.getCount());
214     }
215 
testRowTooBig()216     public void testRowTooBig() {
217         mDatabase.execSQL("CREATE TABLE Tst (Txt BLOB NOT NULL);");
218         byte[] testArr = new byte[10000];
219         Arrays.fill(testArr, (byte) 1);
220         for (int i = 0; i < 10; i++) {
221             mDatabase.execSQL("INSERT INTO Tst VALUES (?)", new Object[]{testArr});
222         }
223 
224         // Now reduce window size, so that no rows can fit
225         Cursor cursor = mDatabase.rawQuery("SELECT * FROM TST", null);
226         CursorWindow cw = new CursorWindow("test", 5000);
227         AbstractWindowedCursor ac = (AbstractWindowedCursor) cursor;
228         ac.setWindow(cw);
229 
230         try {
231             ac.moveToNext();
232             fail("Exception is expected when row exceeds CursorWindow size");
233         } catch (SQLiteBlobTooBigException expected) {
234         }
235     }
236 
testFillWindowForwardOnly()237     public void testFillWindowForwardOnly() {
238         mDatabase.execSQL("CREATE TABLE Tst (Num Integer NOT NULL);");
239         mDatabase.beginTransaction();
240         for (int i = 0; i < 100; i++) {
241             mDatabase.execSQL("INSERT INTO Tst VALUES (?)", new Object[]{i});
242         }
243         mDatabase.setTransactionSuccessful();
244         mDatabase.endTransaction();
245         Cursor cursor = mDatabase.rawQuery("SELECT * FROM TST", null);
246         SQLiteCursor ac = (SQLiteCursor) cursor;
247         CursorWindow window = new CursorWindow("test", 1000);
248         ac.setFillWindowForwardOnly(true);
249         ac.setWindow(window);
250         assertTrue(ac.moveToFirst());
251         // Now skip 70 rows and check that the window start position corresponds to row 70
252         ac.move(70);
253         assertEquals(70, window.getStartPosition());
254     }
255 
testOnMove()256     public void testOnMove() {
257         // Do not test this API. It is callback which:
258         // 1. The callback mechanism has been tested in super class
259         // 2. The functionality is implementation details, no need to test
260     }
261 
createTable(String tableName, String columnNames)262     private void createTable(String tableName, String columnNames) {
263         String sql = "Create TABLE " + tableName + " (_id INTEGER PRIMARY KEY, "
264                 + columnNames + " );";
265         mDatabase.execSQL(sql);
266     }
267 
addValuesIntoTable(String tableName, int start, int end)268     private void addValuesIntoTable(String tableName, int start, int end) {
269         for (int i = start; i <= end; i++) {
270             mDatabase.execSQL("INSERT INTO " + tableName + "(number_1) VALUES ('" + i + "');");
271         }
272     }
273 
getCursor()274     private SQLiteCursor getCursor() {
275         SQLiteCursor cursor = (SQLiteCursor) mDatabase.query(TABLE_NAME, null, null,
276                 null, null, null, null);
277         return cursor;
278     }
279 
280     private class MockObserver extends DataSetObserver {
281         private boolean mHasChanged = false;
282         private boolean mHasInvalidated = false;
283 
284         @Override
onChanged()285         public void onChanged() {
286             super.onChanged();
287             mHasChanged = true;
288         }
289 
290         @Override
onInvalidated()291         public void onInvalidated() {
292             super.onInvalidated();
293             mHasInvalidated = true;
294         }
295 
resetStatus()296         protected void resetStatus() {
297             mHasChanged = false;
298             mHasInvalidated = false;
299         }
300 
hasChanged()301         protected boolean hasChanged() {
302             return mHasChanged;
303         }
304 
hasInvalidated()305         protected boolean hasInvalidated () {
306             return mHasInvalidated;
307         }
308     }
309 
310     private class MockCursorWindow extends CursorWindow {
311         private boolean mIsClosed = false;
312 
MockCursorWindow(boolean localWindow)313         public MockCursorWindow(boolean localWindow) {
314             super(localWindow);
315         }
316 
317         @Override
close()318         public void close() {
319             super.close();
320             mIsClosed = true;
321         }
322 
isClosed()323         public boolean isClosed() {
324             return mIsClosed;
325         }
326     }
327 }
328