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.widget.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.content.Context; 28 import android.content.res.Resources.Theme; 29 import android.database.ContentObserver; 30 import android.database.Cursor; 31 import android.database.DataSetObserver; 32 import android.database.sqlite.SQLiteDatabase; 33 import android.os.Looper; 34 import android.support.test.InstrumentationRegistry; 35 import android.support.test.annotation.UiThreadTest; 36 import android.support.test.filters.MediumTest; 37 import android.support.test.runner.AndroidJUnit4; 38 import android.view.LayoutInflater; 39 import android.view.View; 40 import android.view.ViewGroup; 41 import android.widget.CursorAdapter; 42 import android.widget.Filter; 43 import android.widget.FilterQueryProvider; 44 import android.widget.TextView; 45 46 import com.android.compatibility.common.util.PollingCheck; 47 import com.android.compatibility.common.util.TestThread; 48 49 import org.junit.After; 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 54 import java.io.File; 55 56 /** 57 * Test {@link CursorAdapter}. 58 */ 59 @MediumTest 60 @RunWith(AndroidJUnit4.class) 61 public class CursorAdapterTest { 62 private static final long TEST_TIME_OUT = 5000; 63 private static final int NUMBER_INDEX = 1; 64 private static final String FIRST_NUMBER = "123"; 65 private static final String SECOND_NUMBER = "5555"; 66 private static final int FIRST_ITEM_ID = 1; 67 private static final int SECOND_ITEM_ID = 2; 68 private static final String[] NUMBER_PROJECTION = new String[] { 69 "_id", // 0 70 "number" // 1 71 }; 72 private SQLiteDatabase mDatabase; 73 private File mDatabaseFile; 74 private Cursor mCursor; 75 private ViewGroup mParent; 76 private MockCursorAdapter mMockCursorAdapter; 77 private Context mContext; 78 79 @Before 80 public void setup() { 81 mContext = InstrumentationRegistry.getTargetContext(); 82 File dbDir = mContext.getDir("tests", Context.MODE_PRIVATE); 83 mDatabaseFile = new File(dbDir, "database_test.db"); 84 if (mDatabaseFile.exists()) { 85 mDatabaseFile.delete(); 86 } 87 mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null); 88 assertNotNull(mDatabase); 89 mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, number TEXT);"); 90 mDatabase.execSQL("INSERT INTO test (number) VALUES ('" + FIRST_NUMBER + "');"); 91 mDatabase.execSQL("INSERT INTO test (number) VALUES ('" + SECOND_NUMBER + "');"); 92 mCursor = mDatabase.query("test", NUMBER_PROJECTION, null, null, null, null, null); 93 assertNotNull(mCursor); 94 95 final LayoutInflater inflater = LayoutInflater.from(mContext); 96 mParent = (ViewGroup) inflater.inflate(R.layout.cursoradapter_host, null); 97 assertNotNull(mParent); 98 } 99 100 @After 101 public void teardown() { 102 if (null != mCursor) { 103 mCursor.close(); 104 mCursor = null; 105 } 106 mDatabase.close(); 107 mDatabaseFile.delete(); 108 } 109 110 @UiThreadTest 111 @Test 112 public void testConstructor() { 113 new MockCursorAdapter(mContext, mCursor); 114 115 new MockCursorAdapter(null, null); 116 117 new MockCursorAdapter(mContext, mCursor, true); 118 119 new MockCursorAdapter(null, null, false); 120 } 121 122 @UiThreadTest 123 @Test 124 public void testInit() { 125 MockCursorAdapter cursorAdapter = new MockCursorAdapter(null, null, false); 126 cursorAdapter.init(null, null, false); 127 assertNull(cursorAdapter.getContext()); 128 assertNull(cursorAdapter.getCursor()); 129 assertFalse(cursorAdapter.getAutoRequery()); 130 assertFalse(cursorAdapter.getDataValid()); 131 assertEquals(-1, cursorAdapter.getRowIDColumn()); 132 133 // add context 134 cursorAdapter.init(mContext, null, false); 135 assertSame(mContext, cursorAdapter.getContext()); 136 assertNull(cursorAdapter.getCursor()); 137 assertFalse(cursorAdapter.getAutoRequery()); 138 assertFalse(cursorAdapter.getDataValid()); 139 assertEquals(-1, cursorAdapter.getRowIDColumn()); 140 141 // add autoRequery 142 cursorAdapter.init(mContext, null, true); 143 assertSame(mContext, cursorAdapter.getContext()); 144 assertNull(cursorAdapter.getCursor()); 145 assertTrue(cursorAdapter.getAutoRequery()); 146 assertFalse(cursorAdapter.getDataValid()); 147 assertEquals(-1, cursorAdapter.getRowIDColumn()); 148 149 // add cursor 150 cursorAdapter.init(mContext, mCursor, true); 151 assertSame(mContext, cursorAdapter.getContext()); 152 assertSame(mCursor, cursorAdapter.getCursor()); 153 assertTrue(cursorAdapter.getAutoRequery()); 154 assertTrue(cursorAdapter.getDataValid()); 155 assertEquals(0, cursorAdapter.getRowIDColumn()); 156 try { 157 mCursor.registerContentObserver(cursorAdapter.getContentObserver()); 158 fail("the ContentObserver has not been registered"); 159 } catch (IllegalStateException e) { 160 } 161 try { 162 mCursor.registerDataSetObserver(cursorAdapter.getDataSetObserver()); 163 fail("the DataSetObserver has not been registered"); 164 } catch (IllegalStateException e) { 165 } 166 } 167 168 @UiThreadTest 169 @Test 170 public void testGetCount() { 171 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null); 172 assertEquals(0, cursorAdapter.getCount()); 173 174 cursorAdapter.changeCursor(mCursor); 175 assertEquals(mCursor.getCount(), cursorAdapter.getCount()); 176 } 177 178 @UiThreadTest 179 @Test 180 public void testAccessCursor() { 181 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null); 182 assertNull(cursorAdapter.getCursor()); 183 184 cursorAdapter.changeCursor(mCursor); 185 assertSame(mCursor, cursorAdapter.getCursor()); 186 187 cursorAdapter.changeCursor(null); 188 assertNull(cursorAdapter.getCursor()); 189 } 190 191 @UiThreadTest 192 @Test 193 public void testConvertToString() { 194 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null); 195 assertEquals("", cursorAdapter.convertToString(null)); 196 197 assertEquals(mCursor.toString(), cursorAdapter.convertToString(mCursor)); 198 } 199 200 @UiThreadTest 201 @Test 202 public void testHasStableIds() { 203 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, mCursor); 204 assertTrue(cursorAdapter.hasStableIds()); 205 206 cursorAdapter = new MockCursorAdapter(null, null); 207 assertTrue(cursorAdapter.hasStableIds()); 208 } 209 210 @UiThreadTest 211 @Test 212 public void testGetView() { 213 TextView textView = new TextView(mContext); 214 textView.setText("getView test"); 215 216 MockCursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null); 217 // null cursor 218 assertFalse(cursorAdapter.getDataValid()); 219 try { 220 cursorAdapter.getView(0, textView, mParent); 221 fail("does not throw IllegalStateException when cursor is invalid"); 222 } catch (IllegalStateException e) { 223 } 224 225 cursorAdapter = new MockCursorAdapter(mContext, mCursor); 226 try { 227 cursorAdapter.getView(100, textView, mParent); 228 fail("does not throw IllegalStateException when position is out of bound"); 229 } catch (IllegalStateException e) { 230 } 231 232 // null convertView 233 TextView retView = (TextView) cursorAdapter.getView(0, null, mParent); 234 assertNotNull(retView); 235 assertEquals(FIRST_NUMBER, retView.getText().toString()); 236 237 // convertView is not null 238 retView = (TextView) cursorAdapter.getView(1, textView, mParent); 239 assertNotNull(retView); 240 assertEquals(SECOND_NUMBER, retView.getText().toString()); 241 } 242 243 @UiThreadTest 244 @Test 245 public void testNewDropDownView() { 246 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, mCursor); 247 // null cursor 248 assertNull(cursorAdapter.newDropDownView(mContext, null, mParent)); 249 250 TextView textView = (TextView) cursorAdapter.newDropDownView(mContext, mCursor, mParent); 251 assertEquals(FIRST_NUMBER, textView.getText().toString()); 252 } 253 254 @UiThreadTest 255 @Test 256 public void testGetDropDownView() { 257 MockCursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null); 258 // null cursor 259 assertFalse(cursorAdapter.getDataValid()); 260 TextView textView = new TextView(mContext); 261 textView.setText("getDropDownView test"); 262 263 assertNull(cursorAdapter.getDropDownView(0, textView, mParent)); 264 265 // null convertView 266 cursorAdapter = new MockCursorAdapter(mContext, mCursor); 267 TextView retView = (TextView) cursorAdapter.getDropDownView(0, null, mParent); 268 assertNotNull(retView); 269 assertEquals(FIRST_NUMBER, retView.getText().toString()); 270 271 // convertView is not null 272 retView = (TextView) cursorAdapter.getDropDownView(1, textView, mParent); 273 assertNotNull(retView); 274 assertEquals(SECOND_NUMBER, retView.getText().toString()); 275 } 276 277 @UiThreadTest 278 @Test 279 public void testAccessDropDownViewTheme() { 280 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null); 281 Theme theme = mContext.getResources().newTheme(); 282 cursorAdapter.setDropDownViewTheme(theme); 283 assertSame(theme, cursorAdapter.getDropDownViewTheme()); 284 } 285 286 @UiThreadTest 287 @Test 288 public void testGetFilter() { 289 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, mCursor); 290 Filter filter = cursorAdapter.getFilter(); 291 assertNotNull(filter); 292 } 293 294 @UiThreadTest 295 @Test 296 public void testGetItem() { 297 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null); 298 // cursor is null 299 assertNull(cursorAdapter.getItem(0)); 300 301 cursorAdapter = new MockCursorAdapter(mContext, mCursor); 302 Cursor c = (Cursor) cursorAdapter.getItem(0); 303 assertNotNull(c); 304 assertEquals(0, c.getPosition()); 305 assertEquals(FIRST_NUMBER, c.getString(NUMBER_INDEX)); 306 307 c = (Cursor) cursorAdapter.getItem(1); 308 assertNotNull(c); 309 assertEquals(1, c.getPosition()); 310 assertEquals(SECOND_NUMBER, c.getString(NUMBER_INDEX)); 311 } 312 313 @UiThreadTest 314 @Test 315 public void testGetItemId() { 316 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null); 317 // cursor is null 318 assertEquals(0, cursorAdapter.getItemId(0)); 319 320 cursorAdapter = new MockCursorAdapter(mContext, mCursor); 321 assertEquals(FIRST_ITEM_ID, cursorAdapter.getItemId(0)); 322 323 assertEquals(SECOND_ITEM_ID, cursorAdapter.getItemId(1)); 324 325 // position is out of bound 326 assertEquals(0, cursorAdapter.getItemId(2)); 327 } 328 329 @UiThreadTest 330 @Test 331 public void testAccessFilterQueryProvider() { 332 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, mCursor); 333 FilterQueryProvider filterProvider = new MockFilterQueryProvider(); 334 assertNotNull(filterProvider); 335 336 assertNull(cursorAdapter.getFilterQueryProvider()); 337 338 cursorAdapter.setFilterQueryProvider(filterProvider); 339 assertSame(filterProvider, cursorAdapter.getFilterQueryProvider()); 340 } 341 342 @UiThreadTest 343 @Test 344 public void testRunQueryOnBackgroundThread() { 345 CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, mCursor); 346 final String constraint = "constraint"; 347 348 // FilterQueryProvider is null, return mCursor 349 assertSame(mCursor, cursorAdapter.runQueryOnBackgroundThread(constraint)); 350 351 FilterQueryProvider filterProvider = new MockFilterQueryProvider(); 352 assertNotNull(filterProvider); 353 cursorAdapter.setFilterQueryProvider(filterProvider); 354 assertNull(cursorAdapter.runQueryOnBackgroundThread(constraint)); 355 } 356 357 @Test 358 public void testOnContentChanged() throws Throwable { 359 new TestThread(() -> { 360 Looper.prepare(); 361 mMockCursorAdapter = new MockCursorAdapter(mContext, mCursor); 362 }).runTest(TEST_TIME_OUT); 363 assertFalse(mMockCursorAdapter.hasContentChanged()); 364 // insert a new row 365 mDatabase.execSQL("INSERT INTO test (number) VALUES ('" + FIRST_NUMBER + "');"); 366 new PollingCheck(TEST_TIME_OUT) { 367 @Override 368 protected boolean check() { 369 return mMockCursorAdapter.hasContentChanged(); 370 } 371 }; 372 } 373 374 private final class MockCursorAdapter extends CursorAdapter { 375 private Context mContext; 376 private boolean mAutoRequery; 377 378 private boolean mContentChanged = false; 379 380 public MockCursorAdapter(Context context, Cursor c) { 381 super(context, c); 382 383 mContext = context; 384 mAutoRequery = false; 385 } 386 387 public MockCursorAdapter(Context context, Cursor c, boolean autoRequery) { 388 super(context, c, autoRequery); 389 390 mContext = context; 391 mAutoRequery = autoRequery; 392 } 393 394 public Context getContext() { 395 return mContext; 396 } 397 398 public boolean getAutoRequery() { 399 return mAutoRequery; 400 } 401 402 public boolean getDataValid() { 403 return mDataValid; 404 } 405 406 public int getRowIDColumn() { 407 return mRowIDColumn; 408 } 409 410 public ContentObserver getContentObserver() { 411 return mChangeObserver; 412 } 413 414 public DataSetObserver getDataSetObserver() { 415 return mDataSetObserver; 416 } 417 418 @Override 419 public void init(Context context, Cursor c, boolean autoRequery) { 420 super.init(context, c, autoRequery); 421 422 mContext = context; 423 mAutoRequery = autoRequery; 424 } 425 426 @Override 427 public void bindView(View view, Context context, Cursor cursor) { 428 if (null == context || null == cursor || null == view) { 429 return; 430 } 431 432 if (view instanceof TextView) { 433 String number = cursor.getString(NUMBER_INDEX); 434 TextView textView = (TextView) view; 435 textView.setText(number); 436 } 437 } 438 439 @Override 440 public View newView(Context context, Cursor cursor, ViewGroup parent) { 441 if (null == context || null == cursor || null == parent) { 442 return null; 443 } 444 445 if (cursor.moveToFirst()) { 446 String number = cursor.getString(NUMBER_INDEX); 447 TextView textView = new TextView(context); 448 textView.setText(number); 449 return textView; 450 } 451 return null; 452 } 453 454 @Override 455 protected void onContentChanged() { 456 super.onContentChanged(); 457 mContentChanged = true; 458 } 459 460 public boolean hasContentChanged() { 461 return mContentChanged; 462 } 463 } 464 465 private final class MockFilterQueryProvider implements FilterQueryProvider { 466 public Cursor runQuery(CharSequence constraint) { 467 return null; 468 } 469 } 470 } 471