• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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