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