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 testSetExtras()379 public void testSetExtras() { 380 Bundle b = new Bundle(); 381 mTestAbstractCursor.setExtras(b); 382 assertSame(b, mTestAbstractCursor.getExtras()); 383 } 384 385 @SuppressWarnings("unchecked") createTestList(int rows, int cols)386 private static ArrayList<ArrayList> createTestList(int rows, int cols) { 387 ArrayList<ArrayList> list = new ArrayList<ArrayList>(); 388 Random ran = new Random(); 389 390 for (int i = 0; i < rows; i++) { 391 ArrayList<Integer> col = new ArrayList<Integer>(); 392 list.add(col); 393 394 for (int j = 0; j < cols; j++) { 395 // generate random number 396 Integer r = ran.nextInt(); 397 col.add(r); 398 } 399 } 400 401 return list; 402 } 403 setupDatabase()404 private void setupDatabase() { 405 File dbDir = getInstrumentation().getTargetContext().getDir("tests", 406 Context.MODE_WORLD_WRITEABLE); 407 mDatabaseFile = new File(dbDir, "database_test.db"); 408 if (mDatabaseFile.exists()) { 409 mDatabaseFile.delete(); 410 } 411 mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null); 412 assertNotNull(mDatabaseFile); 413 mDatabase.execSQL("CREATE TABLE test1 (_id INTEGER PRIMARY KEY, number TEXT);"); 414 generateData(); 415 mDatabaseCursor = (AbstractCursor) mDatabase.query("test1", null, null, null, null, null, 416 null); 417 } 418 generateData()419 private void generateData() { 420 for ( int i = 0; i < DATA_COUNT; i++) { 421 mDatabase.execSQL("INSERT INTO test1 (number) VALUES ('" + i + "');"); 422 } 423 } 424 425 private class TestAbstractCursor extends AbstractCursor { 426 private boolean mOnMoveReturnValue; 427 private int mOldPosition; 428 private int mNewPosition; 429 private String[] mColumnNames; 430 private ArrayList<Object>[] mRows; 431 private boolean mHadCalledOnChange = false; 432 TestAbstractCursor()433 public TestAbstractCursor() { 434 super(); 435 } 436 @SuppressWarnings("unchecked") TestAbstractCursor(String[] columnNames, ArrayList<ArrayList> rows)437 public TestAbstractCursor(String[] columnNames, ArrayList<ArrayList> rows) { 438 int colCount = columnNames.length; 439 boolean foundID = false; 440 441 // Add an _id column if not in columnNames 442 for (int i = 0; i < colCount; ++i) { 443 if (columnNames[i].compareToIgnoreCase("_id") == 0) { 444 mColumnNames = columnNames; 445 foundID = true; 446 break; 447 } 448 } 449 450 if (!foundID) { 451 mColumnNames = new String[colCount + 1]; 452 System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length); 453 mColumnNames[colCount] = "_id"; 454 } 455 456 int rowCount = rows.size(); 457 mRows = new ArrayList[rowCount]; 458 459 for (int i = 0; i < rowCount; ++i) { 460 mRows[i] = rows.get(i); 461 462 if (!foundID) { 463 mRows[i].add(Long.valueOf(i)); 464 } 465 } 466 } 467 getOnMoveRet()468 public boolean getOnMoveRet() { 469 return mOnMoveReturnValue; 470 } 471 resetOnMoveRet()472 public void resetOnMoveRet() { 473 mOnMoveReturnValue = false; 474 } 475 getOldPos()476 public int getOldPos() { 477 return mOldPosition; 478 } 479 getNewPos()480 public int getNewPos() { 481 return mNewPosition; 482 } 483 484 @Override onMove(int oldPosition, int newPosition)485 public boolean onMove(int oldPosition, int newPosition) { 486 mOnMoveReturnValue = super.onMove(oldPosition, newPosition); 487 mOldPosition = oldPosition; 488 mNewPosition = newPosition; 489 return mOnMoveReturnValue; 490 } 491 492 @Override getCount()493 public int getCount() { 494 return mRows.length; 495 } 496 497 @Override getColumnNames()498 public String[] getColumnNames() { 499 return mColumnNames; 500 } 501 502 @Override getString(int columnIndex)503 public String getString(int columnIndex) { 504 Object cell = mRows[mPos].get(columnIndex); 505 return (cell == null) ? null : cell.toString(); 506 } 507 508 @Override getShort(int columnIndex)509 public short getShort(int columnIndex) { 510 Number num = (Number) mRows[mPos].get(columnIndex); 511 return num.shortValue(); 512 } 513 514 @Override getInt(int columnIndex)515 public int getInt(int columnIndex) { 516 Number num = (Number) mRows[mPos].get(columnIndex); 517 return num.intValue(); 518 } 519 520 @Override getLong(int columnIndex)521 public long getLong(int columnIndex) { 522 Number num = (Number) mRows[mPos].get(columnIndex); 523 return num.longValue(); 524 } 525 526 @Override getFloat(int columnIndex)527 public float getFloat(int columnIndex) { 528 Number num = (Number) mRows[mPos].get(columnIndex); 529 return num.floatValue(); 530 } 531 532 @Override getDouble(int columnIndex)533 public double getDouble(int columnIndex) { 534 Number num = (Number) mRows[mPos].get(columnIndex); 535 return num.doubleValue(); 536 } 537 538 @Override isNull(int column)539 public boolean isNull(int column) { 540 return false; 541 } 542 hadCalledOnChange()543 public boolean hadCalledOnChange() { 544 return mHadCalledOnChange; 545 } 546 547 // the following are protected methods 548 @Override checkPosition()549 protected void checkPosition() { 550 super.checkPosition(); 551 } 552 553 @Override getUpdatedField(int columnIndex)554 protected Object getUpdatedField(int columnIndex) { 555 return super.getUpdatedField(columnIndex); 556 } 557 558 @Override isFieldUpdated(int columnIndex)559 protected boolean isFieldUpdated(int columnIndex) { 560 return super.isFieldUpdated(columnIndex); 561 } 562 563 @Override onChange(boolean selfChange)564 protected void onChange(boolean selfChange) { 565 super.onChange(selfChange); 566 mHadCalledOnChange = true; 567 } 568 } 569 570 private class MockContentObserver extends ContentObserver { 571 public boolean mHadCalledOnChange; 572 MockContentObserver()573 public MockContentObserver() { 574 super(null); 575 } 576 577 @Override onChange(boolean selfChange)578 public void onChange(boolean selfChange) { 579 super.onChange(selfChange); 580 mHadCalledOnChange = true; 581 synchronized(mLockObj) { 582 mLockObj.notify(); 583 } 584 } 585 586 @Override deliverSelfNotifications()587 public boolean deliverSelfNotifications() { 588 return true; 589 } 590 hadCalledOnChange()591 public boolean hadCalledOnChange() { 592 return mHadCalledOnChange; 593 } 594 } 595 596 private class MockDataSetObserver extends DataSetObserver { 597 private boolean mHadCalledOnChanged; 598 private boolean mHadCalledOnInvalid; 599 600 @Override onChanged()601 public void onChanged() { 602 super.onChanged(); 603 mHadCalledOnChanged = true; 604 } 605 606 @Override onInvalidated()607 public void onInvalidated() { 608 super.onInvalidated(); 609 mHadCalledOnInvalid = true; 610 } 611 hadCalledOnChanged()612 public boolean hadCalledOnChanged() { 613 return mHadCalledOnChanged; 614 } 615 hadCalledOnInvalid()616 public boolean hadCalledOnInvalid() { 617 return mHadCalledOnInvalid; 618 } 619 } 620 } 621