Home | History | Annotate | Download | only in cts
      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
     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
     59     protected void tearDown() throws Exception {
     60         mDatabase.close();
     61         getContext().deleteDatabase(DATABASE_FILE);
     62         super.tearDown();
     63     }
     64 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    285         public void onChanged() {
    286             super.onChanged();
    287             mHasChanged = true;
    288         }
    289 
    290         @Override
    291         public void onInvalidated() {
    292             super.onInvalidated();
    293             mHasInvalidated = true;
    294         }
    295 
    296         protected void resetStatus() {
    297             mHasChanged = false;
    298             mHasInvalidated = false;
    299         }
    300 
    301         protected boolean hasChanged() {
    302             return mHasChanged;
    303         }
    304 
    305         protected boolean hasInvalidated () {
    306             return mHasInvalidated;
    307         }
    308     }
    309 
    310     private class MockCursorWindow extends CursorWindow {
    311         private boolean mIsClosed = false;
    312 
    313         public MockCursorWindow(boolean localWindow) {
    314             super(localWindow);
    315         }
    316 
    317         @Override
    318         public void close() {
    319             super.close();
    320             mIsClosed = true;
    321         }
    322 
    323         public boolean isClosed() {
    324             return mIsClosed;
    325         }
    326     }
    327 }
    328