1 /* 2 * Copyright (C) 2008 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.cts; 18 19 20 import android.content.Context; 21 import android.database.AbstractCursor; 22 import android.database.CharArrayBuffer; 23 import android.database.ContentObserver; 24 import android.database.CursorIndexOutOfBoundsException; 25 import android.database.CursorWindow; 26 import android.database.DataSetObserver; 27 import android.database.sqlite.SQLiteDatabase; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.test.InstrumentationTestCase; 31 32 import java.io.File; 33 import java.util.ArrayList; 34 import java.util.Random; 35 36 /** 37 * Test {@link AbstractCursor}. 38 */ 39 public class AbstractCursorTest extends InstrumentationTestCase { 40 private static final int POSITION0 = 0; 41 private static final int POSITION1 = 1; 42 private static final int ROW_MAX = 10; 43 private static final int DATA_COUNT = 10; 44 private static final String[] COLUMN_NAMES1 = new String[] { 45 "_id", // 0 46 "number" // 1 47 }; 48 private static final String[] COLUMN_NAMES = new String[] { "name", "number", "profit" }; 49 private TestAbstractCursor mTestAbstractCursor; 50 private Object mLockObj = new Object(); 51 52 private SQLiteDatabase mDatabase; 53 private File mDatabaseFile; 54 private AbstractCursor mDatabaseCursor; 55 56 @Override setUp()57 protected void setUp() throws Exception { 58 super.setUp(); 59 60 setupDatabase(); 61 ArrayList<ArrayList> list = createTestList(ROW_MAX, COLUMN_NAMES.length); 62 mTestAbstractCursor = new TestAbstractCursor(COLUMN_NAMES, list); 63 } 64 65 @Override tearDown()66 protected void tearDown() throws Exception { 67 mDatabaseCursor.close(); 68 mTestAbstractCursor.close(); 69 mDatabase.close(); 70 if (mDatabaseFile.exists()) { 71 mDatabaseFile.delete(); 72 } 73 super.tearDown(); 74 } 75 testConstructor()76 public void testConstructor() { 77 TestAbstractCursor abstractCursor = new TestAbstractCursor(); 78 assertEquals(-1, abstractCursor.getPosition()); 79 } 80 testGetBlob()81 public void testGetBlob() { 82 try { 83 mTestAbstractCursor.getBlob(0); 84 fail("getBlob should throws a UnsupportedOperationException here"); 85 } catch (UnsupportedOperationException e) { 86 // expected 87 } 88 } 89 testRegisterDataSetObserver()90 public void testRegisterDataSetObserver() { 91 MockDataSetObserver datasetObserver = new MockDataSetObserver(); 92 93 try { 94 mDatabaseCursor.unregisterDataSetObserver(datasetObserver); 95 fail("Can't unregister DataSetObserver before it is registered."); 96 } catch (IllegalStateException e) { 97 // expected 98 } 99 100 mDatabaseCursor.registerDataSetObserver(datasetObserver); 101 102 try { 103 mDatabaseCursor.registerDataSetObserver(datasetObserver); 104 fail("Can't register DataSetObserver twice before unregister it."); 105 } catch (IllegalStateException e) { 106 // expected 107 } 108 109 mDatabaseCursor.unregisterDataSetObserver(datasetObserver); 110 mDatabaseCursor.registerDataSetObserver(datasetObserver); 111 } 112 testRegisterContentObserver()113 public void testRegisterContentObserver() { 114 MockContentObserver contentObserver = new MockContentObserver(); 115 116 try { 117 mDatabaseCursor.unregisterContentObserver(contentObserver); 118 fail("Can't unregister ContentObserver before it is registered."); 119 } catch (IllegalStateException e) { 120 // expected 121 } 122 123 mDatabaseCursor.registerContentObserver(contentObserver); 124 125 try { 126 mDatabaseCursor.registerContentObserver(contentObserver); 127 fail("Can't register DataSetObserver twice before unregister it."); 128 } catch (IllegalStateException e) { 129 // expected 130 } 131 132 mDatabaseCursor.unregisterContentObserver(contentObserver); 133 mDatabaseCursor.registerContentObserver(contentObserver); 134 } 135 testSetNotificationUri()136 public void testSetNotificationUri() { 137 String MOCK_URI = "content://abstractrcursortest/testtable"; 138 mDatabaseCursor.setNotificationUri(getInstrumentation().getContext().getContentResolver(), 139 Uri.parse(MOCK_URI)); 140 } 141 testRespond()142 public void testRespond() { 143 Bundle b = new Bundle(); 144 Bundle bundle = mDatabaseCursor.respond(b); 145 assertSame(Bundle.EMPTY, bundle); 146 147 bundle = mDatabaseCursor.respond(null); 148 assertSame(Bundle.EMPTY, bundle); 149 } 150 testRequery()151 public void testRequery() { 152 MockDataSetObserver mock = new MockDataSetObserver(); 153 mDatabaseCursor.registerDataSetObserver(mock); 154 assertFalse(mock.hadCalledOnChanged()); 155 mDatabaseCursor.requery(); 156 assertTrue(mock.hadCalledOnChanged()); 157 } 158 testOnChange()159 public void testOnChange() throws InterruptedException { 160 MockContentObserver mock = new MockContentObserver(); 161 mTestAbstractCursor.registerContentObserver(mock); 162 assertFalse(mock.hadCalledOnChange()); 163 mTestAbstractCursor.onChange(true); 164 synchronized(mLockObj) { 165 if ( !mock.hadCalledOnChange() ) { 166 mLockObj.wait(5000); 167 } 168 } 169 assertTrue(mock.hadCalledOnChange()); 170 } 171 testOnMove()172 public void testOnMove() { 173 mTestAbstractCursor.resetOnMoveRet(); 174 assertFalse(mTestAbstractCursor.getOnMoveRet()); 175 mTestAbstractCursor.moveToFirst(); 176 mTestAbstractCursor.moveToPosition(5); 177 assertTrue(mTestAbstractCursor.getOnMoveRet()); 178 assertEquals(0, mTestAbstractCursor.getOldPos()); 179 assertEquals(5, mTestAbstractCursor.getNewPos()); 180 } 181 testMoveToPrevious()182 public void testMoveToPrevious() { 183 // Test moveToFirst, isFirst, moveToNext, getPosition 184 assertTrue(mDatabaseCursor.moveToFirst()); 185 assertTrue(mDatabaseCursor.isFirst()); 186 assertEquals(0, mDatabaseCursor.getPosition()); 187 assertTrue(mDatabaseCursor.moveToNext()); 188 assertEquals(1, mDatabaseCursor.getPosition()); 189 assertFalse(mDatabaseCursor.isFirst()); 190 assertTrue(mDatabaseCursor.moveToNext()); 191 assertEquals(2, mDatabaseCursor.getPosition()); 192 193 // invoke moveToPosition with a number larger than row count. 194 assertFalse(mDatabaseCursor.moveToPosition(30000)); 195 assertEquals(mDatabaseCursor.getCount(), mDatabaseCursor.getPosition()); 196 197 assertFalse(mDatabaseCursor.moveToPosition(-1)); 198 assertEquals(-1, mDatabaseCursor.getPosition()); 199 assertTrue(mDatabaseCursor.isBeforeFirst()); 200 201 mDatabaseCursor.moveToPosition(5); 202 assertEquals(5, mDatabaseCursor.getPosition()); 203 204 // Test moveToPrevious 205 assertTrue(mDatabaseCursor.moveToPrevious()); 206 assertEquals(4, mDatabaseCursor.getPosition()); 207 assertTrue(mDatabaseCursor.moveToPrevious()); 208 assertEquals(3, mDatabaseCursor.getPosition()); 209 assertTrue(mDatabaseCursor.moveToPrevious()); 210 assertEquals(2, mDatabaseCursor.getPosition()); 211 212 // Test moveToLast, isLast, moveToPrevius, isAfterLast. 213 assertFalse(mDatabaseCursor.isLast()); 214 assertTrue(mDatabaseCursor.moveToLast()); 215 assertTrue(mDatabaseCursor.isLast()); 216 assertFalse(mDatabaseCursor.isAfterLast()); 217 218 assertFalse(mDatabaseCursor.moveToNext()); 219 assertTrue(mDatabaseCursor.isAfterLast()); 220 assertFalse(mDatabaseCursor.moveToNext()); 221 assertTrue(mDatabaseCursor.isAfterLast()); 222 assertFalse(mDatabaseCursor.isLast()); 223 assertTrue(mDatabaseCursor.moveToPrevious()); 224 assertTrue(mDatabaseCursor.isLast()); 225 assertTrue(mDatabaseCursor.moveToPrevious()); 226 assertFalse(mDatabaseCursor.isLast()); 227 228 // Test move(int). 229 mDatabaseCursor.moveToFirst(); 230 assertEquals(0, mDatabaseCursor.getPosition()); 231 assertFalse(mDatabaseCursor.move(-1)); 232 assertEquals(-1, mDatabaseCursor.getPosition()); 233 assertTrue(mDatabaseCursor.move(1)); 234 assertEquals(0, mDatabaseCursor.getPosition()); 235 236 assertTrue(mDatabaseCursor.move(5)); 237 assertEquals(5, mDatabaseCursor.getPosition()); 238 assertTrue(mDatabaseCursor.move(-1)); 239 assertEquals(4, mDatabaseCursor.getPosition()); 240 241 mDatabaseCursor.moveToLast(); 242 assertTrue(mDatabaseCursor.isLast()); 243 assertFalse(mDatabaseCursor.isAfterLast()); 244 assertFalse(mDatabaseCursor.move(1)); 245 assertFalse(mDatabaseCursor.isLast()); 246 assertTrue(mDatabaseCursor.isAfterLast()); 247 assertTrue(mDatabaseCursor.move(-1)); 248 assertTrue(mDatabaseCursor.isLast()); 249 assertFalse(mDatabaseCursor.isAfterLast()); 250 } 251 testIsClosed()252 public void testIsClosed() { 253 assertFalse(mDatabaseCursor.isClosed()); 254 mDatabaseCursor.close(); 255 assertTrue(mDatabaseCursor.isClosed()); 256 } 257 testGetWindow()258 public void testGetWindow() { 259 CursorWindow window = new CursorWindow(false); 260 assertEquals(0, window.getNumRows()); 261 // fill window from position 0 262 mDatabaseCursor.fillWindow(0, window); 263 264 assertNotNull(mDatabaseCursor.getWindow()); 265 assertEquals(mDatabaseCursor.getCount(), window.getNumRows()); 266 267 while (mDatabaseCursor.moveToNext()) { 268 assertEquals(mDatabaseCursor.getInt(POSITION1), 269 window.getInt(mDatabaseCursor.getPosition(), POSITION1)); 270 } 271 window.clear(); 272 } 273 testGetWantsAllOnMoveCalls()274 public void testGetWantsAllOnMoveCalls() { 275 assertFalse(mDatabaseCursor.getWantsAllOnMoveCalls()); 276 } 277 testIsFieldUpdated()278 public void testIsFieldUpdated() { 279 mTestAbstractCursor.moveToFirst(); 280 assertFalse(mTestAbstractCursor.isFieldUpdated(0)); 281 } 282 testGetUpdatedField()283 public void testGetUpdatedField() { 284 mTestAbstractCursor.moveToFirst(); 285 assertNull(mTestAbstractCursor.getUpdatedField(0)); 286 } 287 testGetExtras()288 public void testGetExtras() { 289 assertSame(Bundle.EMPTY, mDatabaseCursor.getExtras()); 290 } 291 testGetCount()292 public void testGetCount() { 293 assertEquals(DATA_COUNT, mDatabaseCursor.getCount()); 294 } 295 testGetColumnNames()296 public void testGetColumnNames() { 297 String[] names = mDatabaseCursor.getColumnNames(); 298 assertEquals(COLUMN_NAMES1.length, names.length); 299 300 for (int i = 0; i < COLUMN_NAMES1.length; i++) { 301 assertEquals(COLUMN_NAMES1[i], names[i]); 302 } 303 } 304 testGetColumnName()305 public void testGetColumnName() { 306 assertEquals(COLUMN_NAMES1[0], mDatabaseCursor.getColumnName(0)); 307 assertEquals(COLUMN_NAMES1[1], mDatabaseCursor.getColumnName(1)); 308 } 309 testGetColumnIndexOrThrow()310 public void testGetColumnIndexOrThrow() { 311 final String COLUMN_FAKE = "fake_name"; 312 assertEquals(POSITION0, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION0])); 313 assertEquals(POSITION1, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION1])); 314 assertEquals(POSITION0, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_NAMES1[POSITION0])); 315 assertEquals(POSITION1, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_NAMES1[POSITION1])); 316 317 try { 318 mDatabaseCursor.getColumnIndexOrThrow(COLUMN_FAKE); 319 fail("IllegalArgumentException expected, but not thrown"); 320 } catch (IllegalArgumentException expected) { 321 // expected 322 } 323 } 324 testGetColumnIndex()325 public void testGetColumnIndex() { 326 assertEquals(POSITION0, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION0])); 327 assertEquals(POSITION1, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION1])); 328 } 329 testGetColumnCount()330 public void testGetColumnCount() { 331 assertEquals(COLUMN_NAMES1.length, mDatabaseCursor.getColumnCount()); 332 } 333 testDeactivate()334 public void testDeactivate() { 335 MockDataSetObserver mock = new MockDataSetObserver(); 336 mDatabaseCursor.registerDataSetObserver(mock); 337 assertFalse(mock.hadCalledOnInvalid()); 338 mDatabaseCursor.deactivate(); 339 assertTrue(mock.hadCalledOnInvalid()); 340 } 341 testCopyStringToBuffer()342 public void testCopyStringToBuffer() { 343 CharArrayBuffer ca = new CharArrayBuffer(1000); 344 mTestAbstractCursor.moveToFirst(); 345 mTestAbstractCursor.copyStringToBuffer(0, ca); 346 CursorWindow window = new CursorWindow(false); 347 mTestAbstractCursor.fillWindow(0, window); 348 349 StringBuffer sb = new StringBuffer(); 350 sb.append(window.getString(0, 0)); 351 String str = mTestAbstractCursor.getString(0); 352 assertEquals(str.length(), ca.sizeCopied); 353 assertEquals(sb.toString(), new String(ca.data, 0, ca.sizeCopied)); 354 } 355 testCheckPosition()356 public void testCheckPosition() { 357 // Test with position = -1. 358 try { 359 mTestAbstractCursor.checkPosition(); 360 fail("copyStringToBuffer() should throws CursorIndexOutOfBoundsException here."); 361 } catch (CursorIndexOutOfBoundsException e) { 362 // expected 363 } 364 365 // Test with position = count. 366 assertTrue(mTestAbstractCursor.moveToPosition(mTestAbstractCursor.getCount() - 1)); 367 mTestAbstractCursor.checkPosition(); 368 369 try { 370 assertFalse(mTestAbstractCursor.moveToPosition(mTestAbstractCursor.getCount())); 371 assertEquals(mTestAbstractCursor.getCount(), mTestAbstractCursor.getPosition()); 372 mTestAbstractCursor.checkPosition(); 373 fail("copyStringToBuffer() should throws CursorIndexOutOfBoundsException here."); 374 } catch (CursorIndexOutOfBoundsException e) { 375 // expected 376 } 377 } 378 379 @SuppressWarnings("unchecked") createTestList(int rows, int cols)380 private static ArrayList<ArrayList> createTestList(int rows, int cols) { 381 ArrayList<ArrayList> list = new ArrayList<ArrayList>(); 382 Random ran = new Random(); 383 384 for (int i = 0; i < rows; i++) { 385 ArrayList<Integer> col = new ArrayList<Integer>(); 386 list.add(col); 387 388 for (int j = 0; j < cols; j++) { 389 // generate random number 390 Integer r = ran.nextInt(); 391 col.add(r); 392 } 393 } 394 395 return list; 396 } 397 setupDatabase()398 private void setupDatabase() { 399 File dbDir = getInstrumentation().getTargetContext().getDir("tests", 400 Context.MODE_WORLD_WRITEABLE); 401 mDatabaseFile = new File(dbDir, "database_test.db"); 402 if (mDatabaseFile.exists()) { 403 mDatabaseFile.delete(); 404 } 405 mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null); 406 assertNotNull(mDatabaseFile); 407 mDatabase.execSQL("CREATE TABLE test1 (_id INTEGER PRIMARY KEY, number TEXT);"); 408 generateData(); 409 mDatabaseCursor = (AbstractCursor) mDatabase.query("test1", null, null, null, null, null, 410 null); 411 } 412 generateData()413 private void generateData() { 414 for ( int i = 0; i < DATA_COUNT; i++) { 415 mDatabase.execSQL("INSERT INTO test1 (number) VALUES ('" + i + "');"); 416 } 417 } 418 419 private class TestAbstractCursor extends AbstractCursor { 420 private boolean mOnMoveReturnValue; 421 private int mOldPosition; 422 private int mNewPosition; 423 private String[] mColumnNames; 424 private ArrayList<Object>[] mRows; 425 private boolean mHadCalledOnChange = false; 426 TestAbstractCursor()427 public TestAbstractCursor() { 428 super(); 429 } 430 @SuppressWarnings("unchecked") TestAbstractCursor(String[] columnNames, ArrayList<ArrayList> rows)431 public TestAbstractCursor(String[] columnNames, ArrayList<ArrayList> rows) { 432 int colCount = columnNames.length; 433 boolean foundID = false; 434 mRowIdColumnIndex = 0; 435 436 // Add an _id column if not in columnNames 437 for (int i = 0; i < colCount; ++i) { 438 if (columnNames[i].compareToIgnoreCase("_id") == 0) { 439 mColumnNames = columnNames; 440 foundID = true; 441 break; 442 } 443 } 444 445 if (!foundID) { 446 mColumnNames = new String[colCount + 1]; 447 System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length); 448 mColumnNames[colCount] = "_id"; 449 } 450 451 int rowCount = rows.size(); 452 mRows = new ArrayList[rowCount]; 453 454 for (int i = 0; i < rowCount; ++i) { 455 mRows[i] = rows.get(i); 456 457 if (!foundID) { 458 mRows[i].add(Long.valueOf(i)); 459 } 460 } 461 } 462 getOnMoveRet()463 public boolean getOnMoveRet() { 464 return mOnMoveReturnValue; 465 } 466 resetOnMoveRet()467 public void resetOnMoveRet() { 468 mOnMoveReturnValue = false; 469 } 470 getOldPos()471 public int getOldPos() { 472 return mOldPosition; 473 } 474 getNewPos()475 public int getNewPos() { 476 return mNewPosition; 477 } 478 479 @Override onMove(int oldPosition, int newPosition)480 public boolean onMove(int oldPosition, int newPosition) { 481 mOnMoveReturnValue = super.onMove(oldPosition, newPosition); 482 mOldPosition = oldPosition; 483 mNewPosition = newPosition; 484 return mOnMoveReturnValue; 485 } 486 487 @Override getCount()488 public int getCount() { 489 return mRows.length; 490 } 491 492 @Override getColumnNames()493 public String[] getColumnNames() { 494 return mColumnNames; 495 } 496 497 @Override getString(int columnIndex)498 public String getString(int columnIndex) { 499 Object cell = mRows[mPos].get(columnIndex); 500 return (cell == null) ? null : cell.toString(); 501 } 502 503 @Override getShort(int columnIndex)504 public short getShort(int columnIndex) { 505 Number num = (Number) mRows[mPos].get(columnIndex); 506 return num.shortValue(); 507 } 508 509 @Override getInt(int columnIndex)510 public int getInt(int columnIndex) { 511 Number num = (Number) mRows[mPos].get(columnIndex); 512 return num.intValue(); 513 } 514 515 @Override getLong(int columnIndex)516 public long getLong(int columnIndex) { 517 Number num = (Number) mRows[mPos].get(columnIndex); 518 return num.longValue(); 519 } 520 521 @Override getFloat(int columnIndex)522 public float getFloat(int columnIndex) { 523 Number num = (Number) mRows[mPos].get(columnIndex); 524 return num.floatValue(); 525 } 526 527 @Override getDouble(int columnIndex)528 public double getDouble(int columnIndex) { 529 Number num = (Number) mRows[mPos].get(columnIndex); 530 return num.doubleValue(); 531 } 532 533 @Override isNull(int column)534 public boolean isNull(int column) { 535 return false; 536 } 537 hadCalledOnChange()538 public boolean hadCalledOnChange() { 539 return mHadCalledOnChange; 540 } 541 542 // the following are protected methods 543 @Override checkPosition()544 protected void checkPosition() { 545 super.checkPosition(); 546 } 547 548 @Override getUpdatedField(int columnIndex)549 protected Object getUpdatedField(int columnIndex) { 550 return super.getUpdatedField(columnIndex); 551 } 552 553 @Override isFieldUpdated(int columnIndex)554 protected boolean isFieldUpdated(int columnIndex) { 555 return super.isFieldUpdated(columnIndex); 556 } 557 558 @Override onChange(boolean selfChange)559 protected void onChange(boolean selfChange) { 560 super.onChange(selfChange); 561 mHadCalledOnChange = true; 562 } 563 } 564 565 private class MockContentObserver extends ContentObserver { 566 public boolean mHadCalledOnChange; 567 MockContentObserver()568 public MockContentObserver() { 569 super(null); 570 } 571 572 @Override onChange(boolean selfChange)573 public void onChange(boolean selfChange) { 574 super.onChange(selfChange); 575 mHadCalledOnChange = true; 576 synchronized(mLockObj) { 577 mLockObj.notify(); 578 } 579 } 580 581 @Override deliverSelfNotifications()582 public boolean deliverSelfNotifications() { 583 return true; 584 } 585 hadCalledOnChange()586 public boolean hadCalledOnChange() { 587 return mHadCalledOnChange; 588 } 589 } 590 591 private class MockDataSetObserver extends DataSetObserver { 592 private boolean mHadCalledOnChanged; 593 private boolean mHadCalledOnInvalid; 594 595 @Override onChanged()596 public void onChanged() { 597 super.onChanged(); 598 mHadCalledOnChanged = true; 599 } 600 601 @Override onInvalidated()602 public void onInvalidated() { 603 super.onInvalidated(); 604 mHadCalledOnInvalid = true; 605 } 606 hadCalledOnChanged()607 public boolean hadCalledOnChanged() { 608 return mHadCalledOnChanged; 609 } 610 hadCalledOnInvalid()611 public boolean hadCalledOnInvalid() { 612 return mHadCalledOnInvalid; 613 } 614 } 615 } 616