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 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertSame; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 27 import android.app.Instrumentation; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.database.AbstractCursor; 31 import android.database.CharArrayBuffer; 32 import android.database.ContentObserver; 33 import android.database.Cursor; 34 import android.database.CursorIndexOutOfBoundsException; 35 import android.database.CursorWindow; 36 import android.database.DataSetObserver; 37 import android.database.sqlite.SQLiteDatabase; 38 import android.net.Uri; 39 import android.os.Bundle; 40 import android.platform.test.annotations.AppModeFull; 41 import android.platform.test.annotations.DisabledOnRavenwood; 42 import android.platform.test.ravenwood.RavenwoodRule; 43 import android.provider.Settings; 44 45 import androidx.test.InstrumentationRegistry; 46 import androidx.test.runner.AndroidJUnit4; 47 48 import org.junit.After; 49 import org.junit.Assert; 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 54 import java.io.File; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.List; 58 import java.util.Random; 59 import java.util.concurrent.CountDownLatch; 60 import java.util.concurrent.TimeUnit; 61 62 /** 63 * Test {@link AbstractCursor}. 64 */ 65 @RunWith(AndroidJUnit4.class) 66 public class AbstractCursorTest { 67 private static final int POSITION0 = 0; 68 private static final int POSITION1 = 1; 69 private static final int ROW_MAX = 10; 70 private static final int DATA_COUNT = 10; 71 private static final String[] COLUMN_NAMES1 = new String[] { 72 "_id", // 0 73 "number" // 1 74 }; 75 private static final String[] COLUMN_NAMES = new String[] { "name", "number", "profit" }; 76 private TestAbstractCursor mTestAbstractCursor; 77 private Object mLockObj = new Object(); 78 79 private SQLiteDatabase mDatabase; 80 private File mDatabaseFile; 81 private AbstractCursor mDatabaseCursor; 82 private Context mContext; 83 84 private static final long ON_CHANGE_TIMEOUT_MS = 5000; 85 86 // This timeout value allows for the 10s delay that occurs if an app moves to the CACHED_EMPTY 87 // process state. 88 // TODO(b/292616209) Remove the possibility of the 10s delay and eliminate this long timeout. 89 private static final long ON_CHANGE_TIMEOUT_LONG_MS = ON_CHANGE_TIMEOUT_MS + 10_000; 90 91 @Before setUp()92 public void setUp() throws Exception { 93 ArrayList<ArrayList> list = createTestList(ROW_MAX, COLUMN_NAMES.length); 94 mTestAbstractCursor = new TestAbstractCursor(COLUMN_NAMES, list); 95 96 if (RavenwoodRule.isOnRavenwood()) return; 97 setupDatabase(); 98 mContext = getInstrumentation().getContext(); 99 } 100 101 @After tearDown()102 public void tearDown() throws Exception { 103 mTestAbstractCursor.close(); 104 105 if (RavenwoodRule.isOnRavenwood()) return; 106 mDatabaseCursor.close(); 107 mDatabase.close(); 108 if (mDatabaseFile.exists()) { 109 mDatabaseFile.delete(); 110 } 111 } 112 getInstrumentation()113 private Instrumentation getInstrumentation() { 114 return InstrumentationRegistry.getInstrumentation(); 115 } 116 117 @Test testConstructor()118 public void testConstructor() { 119 TestAbstractCursor abstractCursor = new TestAbstractCursor(); 120 assertEquals(-1, abstractCursor.getPosition()); 121 } 122 123 @Test testGetBlob()124 public void testGetBlob() { 125 try { 126 mTestAbstractCursor.getBlob(0); 127 fail("getBlob should throws a UnsupportedOperationException here"); 128 } catch (UnsupportedOperationException e) { 129 // expected 130 } 131 } 132 133 @Test testRegisterDataSetObserver()134 public void testRegisterDataSetObserver() { 135 MockDataSetObserver datasetObserver = new MockDataSetObserver(); 136 137 try { 138 mTestAbstractCursor.unregisterDataSetObserver(datasetObserver); 139 fail("Can't unregister DataSetObserver before it is registered."); 140 } catch (IllegalStateException e) { 141 // expected 142 } 143 144 mTestAbstractCursor.registerDataSetObserver(datasetObserver); 145 146 try { 147 mTestAbstractCursor.registerDataSetObserver(datasetObserver); 148 fail("Can't register DataSetObserver twice before unregister it."); 149 } catch (IllegalStateException e) { 150 // expected 151 } 152 153 mTestAbstractCursor.unregisterDataSetObserver(datasetObserver); 154 mTestAbstractCursor.registerDataSetObserver(datasetObserver); 155 } 156 157 @Test testRegisterContentObserver()158 public void testRegisterContentObserver() { 159 MockContentObserver contentObserver = new MockContentObserver(); 160 161 try { 162 mTestAbstractCursor.unregisterContentObserver(contentObserver); 163 fail("Can't unregister ContentObserver before it is registered."); 164 } catch (IllegalStateException e) { 165 // expected 166 } 167 168 mTestAbstractCursor.registerContentObserver(contentObserver); 169 170 try { 171 mTestAbstractCursor.registerContentObserver(contentObserver); 172 fail("Can't register DataSetObserver twice before unregister it."); 173 } catch (IllegalStateException e) { 174 // expected 175 } 176 177 mTestAbstractCursor.unregisterContentObserver(contentObserver); 178 mTestAbstractCursor.registerContentObserver(contentObserver); 179 } 180 181 @Test 182 @DisabledOnRavenwood(blockedBy = ContentResolver.class) testSetNotificationUri()183 public void testSetNotificationUri() { 184 final Uri testUri = Settings.System.getUriFor(Settings.System.TIME_12_24); 185 mDatabaseCursor.setNotificationUri(getInstrumentation().getContext().getContentResolver(), 186 testUri); 187 } 188 189 @Test 190 @DisabledOnRavenwood(blockedBy = ContentResolver.class) testSetNotificationUris_selfNotify()191 public void testSetNotificationUris_selfNotify() throws Exception { 192 final Uri testUri1 = Settings.System.getUriFor(Settings.System.TIME_12_24); 193 final Uri testUri2 = Settings.Global.getUriFor( 194 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED); 195 mDatabaseCursor.setNotificationUris(mContext.getContentResolver(), 196 Arrays.asList(testUri1, testUri2)); 197 final MockContentObserver observer = new MockContentObserver(); 198 mDatabaseCursor.registerContentObserver(observer); 199 200 mContext.getContentResolver().notifyChange(testUri1, null); 201 observer.waitForOnChange(ON_CHANGE_TIMEOUT_MS); 202 observer.resetOnChangeWatch(); 203 mContext.getContentResolver().notifyChange(testUri2, null); 204 observer.waitForOnChange(ON_CHANGE_TIMEOUT_MS); 205 } 206 207 @Test 208 @AppModeFull 209 @DisabledOnRavenwood(blockedBy = ContentResolver.class) testSetNotificationsUris()210 public void testSetNotificationsUris() throws Exception { 211 final Uri queryUri = Uri.parse("content://com.android.cts.providerapp"); 212 try (Cursor cursor = mContext.getContentResolver().query(queryUri, null, null, null)) { 213 final MockContentObserver observer = new MockContentObserver(); 214 cursor.registerContentObserver(observer); 215 216 mContext.getContentResolver().call(queryUri, "notify", "1", null); 217 observer.waitForOnChange(ON_CHANGE_TIMEOUT_LONG_MS); 218 observer.resetOnChangeWatch(); 219 mContext.getContentResolver().call(queryUri, "notify", "2", null); 220 observer.waitForOnChange(ON_CHANGE_TIMEOUT_LONG_MS); 221 } 222 } 223 224 @Test 225 @DisabledOnRavenwood(blockedBy = ContentResolver.class) testGetNotificationUris()226 public void testGetNotificationUris() throws Exception { 227 final Uri[] notificationUris = new Uri[] { 228 Settings.Global.getUriFor(Settings.Global.MODE_RINGER), 229 Settings.Global.getUriFor(Settings.Global.BOOT_COUNT) 230 }; 231 mDatabaseCursor.setNotificationUris(mContext.getContentResolver(), 232 Arrays.asList(notificationUris)); 233 final List<Uri> actualNotificationUris = mDatabaseCursor.getNotificationUris(); 234 Assert.assertArrayEquals(notificationUris, actualNotificationUris.toArray(new Uri[0])); 235 } 236 237 @Test testRespond()238 public void testRespond() { 239 Bundle b = new Bundle(); 240 Bundle bundle = mTestAbstractCursor.respond(b); 241 assertSame(Bundle.EMPTY, bundle); 242 243 bundle = mTestAbstractCursor.respond(null); 244 assertSame(Bundle.EMPTY, bundle); 245 } 246 247 @Test testRequery()248 public void testRequery() { 249 MockDataSetObserver mock = new MockDataSetObserver(); 250 mTestAbstractCursor.registerDataSetObserver(mock); 251 assertFalse(mock.hadCalledOnChanged()); 252 mTestAbstractCursor.requery(); 253 assertTrue(mock.hadCalledOnChanged()); 254 } 255 256 @Test testOnChange()257 public void testOnChange() throws InterruptedException { 258 MockContentObserver mock = new MockContentObserver(); 259 mTestAbstractCursor.registerContentObserver(mock); 260 mTestAbstractCursor.onChange(true); 261 mock.waitForOnChange(ON_CHANGE_TIMEOUT_MS); 262 } 263 264 @Test testOnMove()265 public void testOnMove() { 266 assertFalse(mTestAbstractCursor.getOnMoveRet()); 267 mTestAbstractCursor.moveToFirst(); 268 assertTrue(mTestAbstractCursor.getOnMoveRet()); 269 assertEquals(1, mTestAbstractCursor.getRowsMovedSum()); 270 271 mTestAbstractCursor.moveToPosition(5); 272 assertTrue(mTestAbstractCursor.getOnMoveRet()); 273 assertEquals(6, mTestAbstractCursor.getRowsMovedSum()); 274 assertEquals(0, mTestAbstractCursor.getOldPos()); 275 assertEquals(5, mTestAbstractCursor.getNewPos()); 276 } 277 278 @Test testOnMove_samePosition()279 public void testOnMove_samePosition() { 280 mTestAbstractCursor.moveToFirst(); 281 mTestAbstractCursor.moveToPosition(5); 282 assertEquals(6, mTestAbstractCursor.getRowsMovedSum()); 283 mTestAbstractCursor.moveToPosition(5); 284 // Moving to the same position should either call onMove(5, 5) 285 // or be a no-op. It should no change the RowsMovedSum. 286 assertEquals(6, mTestAbstractCursor.getRowsMovedSum()); 287 } 288 289 @Test testMoveToPrevious()290 public void testMoveToPrevious() { 291 // Test moveToFirst, isFirst, moveToNext, getPosition 292 assertTrue(mTestAbstractCursor.moveToFirst()); 293 assertTrue(mTestAbstractCursor.isFirst()); 294 assertEquals(0, mTestAbstractCursor.getPosition()); 295 assertTrue(mTestAbstractCursor.moveToNext()); 296 assertEquals(1, mTestAbstractCursor.getPosition()); 297 assertFalse(mTestAbstractCursor.isFirst()); 298 assertTrue(mTestAbstractCursor.moveToNext()); 299 assertEquals(2, mTestAbstractCursor.getPosition()); 300 301 // invoke moveToPosition with a number larger than row count. 302 assertFalse(mTestAbstractCursor.moveToPosition(30000)); 303 assertEquals(mTestAbstractCursor.getCount(), mTestAbstractCursor.getPosition()); 304 305 assertFalse(mTestAbstractCursor.moveToPosition(-1)); 306 assertEquals(-1, mTestAbstractCursor.getPosition()); 307 assertTrue(mTestAbstractCursor.isBeforeFirst()); 308 309 mTestAbstractCursor.moveToPosition(5); 310 assertEquals(5, mTestAbstractCursor.getPosition()); 311 312 // Test moveToPrevious 313 assertTrue(mTestAbstractCursor.moveToPrevious()); 314 assertEquals(4, mTestAbstractCursor.getPosition()); 315 assertTrue(mTestAbstractCursor.moveToPrevious()); 316 assertEquals(3, mTestAbstractCursor.getPosition()); 317 assertTrue(mTestAbstractCursor.moveToPrevious()); 318 assertEquals(2, mTestAbstractCursor.getPosition()); 319 320 // Test moveToLast, isLast, moveToPrevius, isAfterLast. 321 assertFalse(mTestAbstractCursor.isLast()); 322 assertTrue(mTestAbstractCursor.moveToLast()); 323 assertTrue(mTestAbstractCursor.isLast()); 324 assertFalse(mTestAbstractCursor.isAfterLast()); 325 326 assertFalse(mTestAbstractCursor.moveToNext()); 327 assertTrue(mTestAbstractCursor.isAfterLast()); 328 assertFalse(mTestAbstractCursor.moveToNext()); 329 assertTrue(mTestAbstractCursor.isAfterLast()); 330 assertFalse(mTestAbstractCursor.isLast()); 331 assertTrue(mTestAbstractCursor.moveToPrevious()); 332 assertTrue(mTestAbstractCursor.isLast()); 333 assertTrue(mTestAbstractCursor.moveToPrevious()); 334 assertFalse(mTestAbstractCursor.isLast()); 335 336 // Test move(int). 337 mTestAbstractCursor.moveToFirst(); 338 assertEquals(0, mTestAbstractCursor.getPosition()); 339 assertFalse(mTestAbstractCursor.move(-1)); 340 assertEquals(-1, mTestAbstractCursor.getPosition()); 341 assertTrue(mTestAbstractCursor.move(1)); 342 assertEquals(0, mTestAbstractCursor.getPosition()); 343 344 assertTrue(mTestAbstractCursor.move(5)); 345 assertEquals(5, mTestAbstractCursor.getPosition()); 346 assertTrue(mTestAbstractCursor.move(-1)); 347 assertEquals(4, mTestAbstractCursor.getPosition()); 348 349 mTestAbstractCursor.moveToLast(); 350 assertTrue(mTestAbstractCursor.isLast()); 351 assertFalse(mTestAbstractCursor.isAfterLast()); 352 assertFalse(mTestAbstractCursor.move(1)); 353 assertFalse(mTestAbstractCursor.isLast()); 354 assertTrue(mTestAbstractCursor.isAfterLast()); 355 assertTrue(mTestAbstractCursor.move(-1)); 356 assertTrue(mTestAbstractCursor.isLast()); 357 assertFalse(mTestAbstractCursor.isAfterLast()); 358 } 359 360 @Test testIsClosed()361 public void testIsClosed() { 362 assertFalse(mTestAbstractCursor.isClosed()); 363 mTestAbstractCursor.close(); 364 assertTrue(mTestAbstractCursor.isClosed()); 365 } 366 367 @Test 368 @DisabledOnRavenwood(blockedBy = CursorWindow.class) testGetWindow()369 public void testGetWindow() { 370 CursorWindow window = new CursorWindow(false); 371 assertEquals(0, window.getNumRows()); 372 // fill window from position 0 373 mDatabaseCursor.fillWindow(0, window); 374 375 assertNotNull(mDatabaseCursor.getWindow()); 376 assertEquals(mDatabaseCursor.getCount(), window.getNumRows()); 377 378 while (mDatabaseCursor.moveToNext()) { 379 assertEquals(mDatabaseCursor.getInt(POSITION1), 380 window.getInt(mDatabaseCursor.getPosition(), POSITION1)); 381 } 382 window.clear(); 383 } 384 385 @Test testGetWantsAllOnMoveCalls()386 public void testGetWantsAllOnMoveCalls() { 387 assertFalse(mTestAbstractCursor.getWantsAllOnMoveCalls()); 388 } 389 390 @Test testIsFieldUpdated()391 public void testIsFieldUpdated() { 392 mTestAbstractCursor.moveToFirst(); 393 assertFalse(mTestAbstractCursor.isFieldUpdated(0)); 394 } 395 396 @Test testGetUpdatedField()397 public void testGetUpdatedField() { 398 mTestAbstractCursor.moveToFirst(); 399 assertNull(mTestAbstractCursor.getUpdatedField(0)); 400 } 401 402 @Test testGetExtras()403 public void testGetExtras() { 404 assertSame(Bundle.EMPTY, mTestAbstractCursor.getExtras()); 405 } 406 407 @Test testGetCount()408 public void testGetCount() { 409 assertEquals(DATA_COUNT, mTestAbstractCursor.getCount()); 410 } 411 412 @Test 413 @DisabledOnRavenwood(blockedBy = SQLiteDatabase.class) testGetColumnNames()414 public void testGetColumnNames() { 415 String[] names = mDatabaseCursor.getColumnNames(); 416 assertEquals(COLUMN_NAMES1.length, names.length); 417 418 for (int i = 0; i < COLUMN_NAMES1.length; i++) { 419 assertEquals(COLUMN_NAMES1[i], names[i]); 420 } 421 } 422 423 @Test 424 @DisabledOnRavenwood(blockedBy = SQLiteDatabase.class) testGetColumnName()425 public void testGetColumnName() { 426 assertEquals(COLUMN_NAMES1[0], mDatabaseCursor.getColumnName(0)); 427 assertEquals(COLUMN_NAMES1[1], mDatabaseCursor.getColumnName(1)); 428 } 429 430 @Test 431 @DisabledOnRavenwood(blockedBy = SQLiteDatabase.class) testGetColumnIndexOrThrow()432 public void testGetColumnIndexOrThrow() { 433 final String COLUMN_FAKE = "fake_name"; 434 assertEquals(POSITION0, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION0])); 435 assertEquals(POSITION1, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION1])); 436 assertEquals(POSITION0, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_NAMES1[POSITION0])); 437 assertEquals(POSITION1, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_NAMES1[POSITION1])); 438 439 try { 440 mDatabaseCursor.getColumnIndexOrThrow(COLUMN_FAKE); 441 fail("IllegalArgumentException expected, but not thrown"); 442 } catch (IllegalArgumentException expected) { 443 // expected 444 } 445 } 446 447 @Test 448 @DisabledOnRavenwood(blockedBy = SQLiteDatabase.class) testGetColumnIndex()449 public void testGetColumnIndex() { 450 assertEquals(POSITION0, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION0])); 451 assertEquals(POSITION1, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION1])); 452 } 453 454 @Test 455 @DisabledOnRavenwood(blockedBy = SQLiteDatabase.class) testGetColumnCount()456 public void testGetColumnCount() { 457 assertEquals(COLUMN_NAMES1.length, mDatabaseCursor.getColumnCount()); 458 } 459 460 @Test testDeactivate()461 public void testDeactivate() { 462 MockDataSetObserver mock = new MockDataSetObserver(); 463 mTestAbstractCursor.registerDataSetObserver(mock); 464 assertFalse(mock.hadCalledOnInvalid()); 465 mTestAbstractCursor.deactivate(); 466 assertTrue(mock.hadCalledOnInvalid()); 467 } 468 469 @Test 470 @DisabledOnRavenwood(blockedBy = CursorWindow.class) testCopyStringToBuffer()471 public void testCopyStringToBuffer() { 472 CharArrayBuffer ca = new CharArrayBuffer(1000); 473 mTestAbstractCursor.moveToFirst(); 474 mTestAbstractCursor.copyStringToBuffer(0, ca); 475 CursorWindow window = new CursorWindow(false); 476 mTestAbstractCursor.fillWindow(0, window); 477 478 StringBuffer sb = new StringBuffer(); 479 sb.append(window.getString(0, 0)); 480 String str = mTestAbstractCursor.getString(0); 481 assertEquals(str.length(), ca.sizeCopied); 482 assertEquals(sb.toString(), new String(ca.data, 0, ca.sizeCopied)); 483 } 484 485 @Test testCheckPosition()486 public void testCheckPosition() { 487 // Test with position = -1. 488 try { 489 mTestAbstractCursor.checkPosition(); 490 fail("copyStringToBuffer() should throws CursorIndexOutOfBoundsException here."); 491 } catch (CursorIndexOutOfBoundsException e) { 492 // expected 493 } 494 495 // Test with position = count. 496 assertTrue(mTestAbstractCursor.moveToPosition(mTestAbstractCursor.getCount() - 1)); 497 mTestAbstractCursor.checkPosition(); 498 499 try { 500 assertFalse(mTestAbstractCursor.moveToPosition(mTestAbstractCursor.getCount())); 501 assertEquals(mTestAbstractCursor.getCount(), mTestAbstractCursor.getPosition()); 502 mTestAbstractCursor.checkPosition(); 503 fail("copyStringToBuffer() should throws CursorIndexOutOfBoundsException here."); 504 } catch (CursorIndexOutOfBoundsException e) { 505 // expected 506 } 507 } 508 509 @Test testSetExtras()510 public void testSetExtras() { 511 Bundle b = new Bundle(); 512 mTestAbstractCursor.setExtras(b); 513 assertSame(b, mTestAbstractCursor.getExtras()); 514 } 515 516 @SuppressWarnings("unchecked") createTestList(int rows, int cols)517 private static ArrayList<ArrayList> createTestList(int rows, int cols) { 518 ArrayList<ArrayList> list = new ArrayList<ArrayList>(); 519 Random ran = new Random(); 520 521 for (int i = 0; i < rows; i++) { 522 ArrayList<Integer> col = new ArrayList<Integer>(); 523 list.add(col); 524 525 for (int j = 0; j < cols; j++) { 526 // generate random number 527 Integer r = ran.nextInt(); 528 col.add(r); 529 } 530 } 531 532 return list; 533 } 534 setupDatabase()535 private void setupDatabase() { 536 File dbDir = getInstrumentation().getTargetContext().getDir("tests", 537 Context.MODE_PRIVATE); 538 mDatabaseFile = new File(dbDir, "database_test.db"); 539 if (mDatabaseFile.exists()) { 540 mDatabaseFile.delete(); 541 } 542 mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null); 543 assertNotNull(mDatabaseFile); 544 mDatabase.execSQL("CREATE TABLE test1 (_id INTEGER PRIMARY KEY, number TEXT);"); 545 generateData(); 546 mDatabaseCursor = (AbstractCursor) mDatabase.query("test1", null, null, null, null, null, 547 null); 548 } 549 generateData()550 private void generateData() { 551 for ( int i = 0; i < DATA_COUNT; i++) { 552 mDatabase.execSQL("INSERT INTO test1 (number) VALUES ('" + i + "');"); 553 } 554 } 555 556 private class TestAbstractCursor extends AbstractCursor { 557 private boolean mOnMoveReturnValue; 558 private int mOldPosition; 559 private int mNewPosition; 560 /** The accumulated number of rows this cursor has moved over. */ 561 private int mRowsMovedSum; 562 private String[] mColumnNames; 563 private ArrayList<Object>[] mRows; 564 private boolean mHadCalledOnChange = false; 565 TestAbstractCursor()566 public TestAbstractCursor() { 567 super(); 568 } 569 @SuppressWarnings("unchecked") TestAbstractCursor(String[] columnNames, ArrayList<ArrayList> rows)570 public TestAbstractCursor(String[] columnNames, ArrayList<ArrayList> rows) { 571 int colCount = columnNames.length; 572 boolean foundID = false; 573 574 // Add an _id column if not in columnNames 575 for (int i = 0; i < colCount; ++i) { 576 if (columnNames[i].compareToIgnoreCase("_id") == 0) { 577 mColumnNames = columnNames; 578 foundID = true; 579 break; 580 } 581 } 582 583 if (!foundID) { 584 mColumnNames = new String[colCount + 1]; 585 System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length); 586 mColumnNames[colCount] = "_id"; 587 } 588 589 int rowCount = rows.size(); 590 mRows = new ArrayList[rowCount]; 591 592 for (int i = 0; i < rowCount; ++i) { 593 mRows[i] = rows.get(i); 594 595 if (!foundID) { 596 mRows[i].add(Long.valueOf(i)); 597 } 598 } 599 } 600 getOnMoveRet()601 public boolean getOnMoveRet() { 602 return mOnMoveReturnValue; 603 } 604 resetOnMoveRet()605 public void resetOnMoveRet() { 606 mOnMoveReturnValue = false; 607 } 608 getOldPos()609 public int getOldPos() { 610 return mOldPosition; 611 } 612 getNewPos()613 public int getNewPos() { 614 return mNewPosition; 615 } 616 getRowsMovedSum()617 public int getRowsMovedSum() { 618 return mRowsMovedSum; 619 } 620 621 @Override onMove(int oldPosition, int newPosition)622 public boolean onMove(int oldPosition, int newPosition) { 623 mOnMoveReturnValue = super.onMove(oldPosition, newPosition); 624 mOldPosition = oldPosition; 625 mNewPosition = newPosition; 626 mRowsMovedSum += Math.abs(newPosition - oldPosition); 627 return mOnMoveReturnValue; 628 } 629 630 @Override getCount()631 public int getCount() { 632 return mRows.length; 633 } 634 635 @Override getColumnNames()636 public String[] getColumnNames() { 637 return mColumnNames; 638 } 639 640 @Override getString(int columnIndex)641 public String getString(int columnIndex) { 642 Object cell = mRows[mPos].get(columnIndex); 643 return (cell == null) ? null : cell.toString(); 644 } 645 646 @Override getShort(int columnIndex)647 public short getShort(int columnIndex) { 648 Number num = (Number) mRows[mPos].get(columnIndex); 649 return num.shortValue(); 650 } 651 652 @Override getInt(int columnIndex)653 public int getInt(int columnIndex) { 654 Number num = (Number) mRows[mPos].get(columnIndex); 655 return num.intValue(); 656 } 657 658 @Override getLong(int columnIndex)659 public long getLong(int columnIndex) { 660 Number num = (Number) mRows[mPos].get(columnIndex); 661 return num.longValue(); 662 } 663 664 @Override getFloat(int columnIndex)665 public float getFloat(int columnIndex) { 666 Number num = (Number) mRows[mPos].get(columnIndex); 667 return num.floatValue(); 668 } 669 670 @Override getDouble(int columnIndex)671 public double getDouble(int columnIndex) { 672 Number num = (Number) mRows[mPos].get(columnIndex); 673 return num.doubleValue(); 674 } 675 676 @Override isNull(int column)677 public boolean isNull(int column) { 678 return false; 679 } 680 hadCalledOnChange()681 public boolean hadCalledOnChange() { 682 return mHadCalledOnChange; 683 } 684 685 // the following are protected methods 686 @Override checkPosition()687 protected void checkPosition() { 688 super.checkPosition(); 689 } 690 691 @Override getUpdatedField(int columnIndex)692 protected Object getUpdatedField(int columnIndex) { 693 return super.getUpdatedField(columnIndex); 694 } 695 696 @Override isFieldUpdated(int columnIndex)697 protected boolean isFieldUpdated(int columnIndex) { 698 return super.isFieldUpdated(columnIndex); 699 } 700 701 @Override onChange(boolean selfChange)702 protected void onChange(boolean selfChange) { 703 super.onChange(selfChange); 704 mHadCalledOnChange = true; 705 } 706 } 707 708 private class MockContentObserver extends ContentObserver { 709 private CountDownLatch mCountDownLatch; 710 MockContentObserver()711 public MockContentObserver() { 712 super(null); 713 714 mCountDownLatch = new CountDownLatch(1); 715 } 716 717 @Override onChange(boolean selfChange)718 public void onChange(boolean selfChange) { 719 super.onChange(selfChange); 720 mCountDownLatch.countDown(); 721 } 722 723 @Override deliverSelfNotifications()724 public boolean deliverSelfNotifications() { 725 return true; 726 } 727 resetOnChangeWatch()728 public void resetOnChangeWatch() { 729 mCountDownLatch = new CountDownLatch(1); 730 } 731 waitForOnChange(long timeoutMs)732 public void waitForOnChange(long timeoutMs) throws InterruptedException { 733 if (!mCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) { 734 fail("Timed out waiting for onChange() to get called"); 735 } 736 } 737 } 738 739 private class MockDataSetObserver extends DataSetObserver { 740 private boolean mHadCalledOnChanged; 741 private boolean mHadCalledOnInvalid; 742 743 @Override onChanged()744 public void onChanged() { 745 super.onChanged(); 746 mHadCalledOnChanged = true; 747 } 748 749 @Override onInvalidated()750 public void onInvalidated() { 751 super.onInvalidated(); 752 mHadCalledOnInvalid = true; 753 } 754 hadCalledOnChanged()755 public boolean hadCalledOnChanged() { 756 return mHadCalledOnChanged; 757 } 758 hadCalledOnInvalid()759 public boolean hadCalledOnInvalid() { 760 return mHadCalledOnInvalid; 761 } 762 } 763 } 764