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 com.android.cts.widget.R; 20 21 22 import android.content.Context; 23 import android.cts.util.WidgetTestUtils; 24 import android.database.Cursor; 25 import android.database.MatrixCursor; 26 import android.graphics.Bitmap; 27 import android.graphics.drawable.BitmapDrawable; 28 import android.test.InstrumentationTestCase; 29 import android.test.UiThreadTest; 30 import android.view.LayoutInflater; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.widget.ImageView; 34 import android.widget.LinearLayout; 35 import android.widget.SimpleCursorAdapter; 36 import android.widget.TextView; 37 import android.widget.SimpleCursorAdapter.CursorToStringConverter; 38 import android.widget.SimpleCursorAdapter.ViewBinder; 39 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.OutputStream; 43 44 /** 45 * Test {@link SimpleCursorAdapter}. 46 * The simple cursor adapter's cursor will be set to 47 * {@link SimpleCursorAdapterTest#mCursor} It will use internal 48 * R.layout.simple_list_item_1. 49 */ 50 public class SimpleCursorAdapterTest extends InstrumentationTestCase { 51 private static final int ADAPTER_ROW_COUNT = 20; 52 53 private static final int DEFAULT_COLUMN_COUNT = 2; 54 55 private static final int[] VIEWS_TO = new int[] { R.id.cursorAdapter_item0 }; 56 57 private static final String[] COLUMNS_FROM = new String[] { "column1" }; 58 59 private static final String SAMPLE_IMAGE_NAME = "testimage.jpg"; 60 61 private Context mContext; 62 63 /** 64 * The original cursor and its content will be set to: 65 * <TABLE> 66 * <TR> 67 * <TH>Column0</TH> 68 * <TH>Column1</TH> 69 * </TR> 70 * <TR> 71 * <TD>00</TD> 72 * <TD>01</TD> 73 * </TR> 74 * <TR> 75 * <TD>10</TD> 76 * <TD>11</TD> 77 * </TR> 78 * <TR> 79 * <TD>...</TD> 80 * <TD>...</TD> 81 * </TR> 82 * <TR> 83 * <TD>190</TD> 84 * <TD>191</TD> 85 * </TR> 86 * </TABLE> 87 * It has 2 columns and 20 rows 88 */ 89 private Cursor mCursor; 90 91 @Override 92 protected void setUp() throws Exception { 93 super.setUp(); 94 mContext = getInstrumentation().getTargetContext(); 95 96 mCursor = createTestCursor(DEFAULT_COLUMN_COUNT, ADAPTER_ROW_COUNT); 97 } 98 99 private SimpleCursorAdapter makeSimpleCursorAdapter() { 100 return new SimpleCursorAdapter( 101 mContext, R.layout.cursoradapter_item0, mCursor, COLUMNS_FROM, VIEWS_TO); 102 } 103 104 @UiThreadTest 105 public void testConstructor() { 106 new SimpleCursorAdapter(mContext, R.layout.cursoradapter_item0, 107 createTestCursor(DEFAULT_COLUMN_COUNT, ADAPTER_ROW_COUNT), 108 COLUMNS_FROM, VIEWS_TO); 109 } 110 111 @UiThreadTest 112 public void testBindView() { 113 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 114 TextView listItem = (TextView) simpleCursorAdapter.newView(mContext, null, null); 115 116 listItem.setText(""); 117 mCursor.moveToFirst(); 118 simpleCursorAdapter.bindView(listItem, null, mCursor); 119 assertEquals("01", listItem.getText().toString()); 120 121 mCursor.moveToLast(); 122 simpleCursorAdapter.bindView(listItem, null, mCursor); 123 assertEquals("191", listItem.getText().toString()); 124 125 // the binder take care of binding 126 listItem.setText(""); 127 MockViewBinder binder = new MockViewBinder(true); 128 simpleCursorAdapter.setViewBinder(binder); 129 binder.reset(); 130 mCursor.moveToFirst(); 131 simpleCursorAdapter.bindView(listItem, null, mCursor); 132 assertTrue(binder.hasCalledSetViewValueCalledCount()); 133 assertEquals("", listItem.getText().toString()); 134 135 // the binder try to bind but fail 136 binder = new MockViewBinder(false); 137 simpleCursorAdapter.setViewBinder(binder); 138 mCursor.moveToLast(); 139 simpleCursorAdapter.bindView(listItem, null, mCursor); 140 assertTrue(binder.hasCalledSetViewValueCalledCount()); 141 assertEquals("191", listItem.getText().toString()); 142 143 final int [] to = { R.id.cursorAdapter_host }; 144 simpleCursorAdapter = new SimpleCursorAdapter(mContext, R.layout.cursoradapter_host, 145 mCursor, COLUMNS_FROM, to); 146 LinearLayout illegalView = (LinearLayout)simpleCursorAdapter.newView(mContext, null, null); 147 try { 148 // The IllegalStateException already gets thrown in the line above. 149 simpleCursorAdapter.bindView(illegalView, null, mCursor); 150 fail("Should throw IllegalStateException if the view is not TextView or ImageView"); 151 } catch (IllegalStateException e) { 152 // expected 153 } 154 } 155 156 @UiThreadTest 157 public void testAccessViewBinder() { 158 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 159 assertNull(simpleCursorAdapter.getViewBinder()); 160 161 MockViewBinder binder = new MockViewBinder(true); 162 simpleCursorAdapter.setViewBinder(binder); 163 assertSame(binder, simpleCursorAdapter.getViewBinder()); 164 165 binder = new MockViewBinder(false); 166 simpleCursorAdapter.setViewBinder(binder); 167 assertSame(binder, simpleCursorAdapter.getViewBinder()); 168 169 simpleCursorAdapter.setViewBinder(null); 170 assertNull(simpleCursorAdapter.getViewBinder()); 171 } 172 173 @UiThreadTest 174 public void testSetViewText() { 175 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 176 TextView view = new TextView(mContext); 177 simpleCursorAdapter.setViewText(view, "expected"); 178 assertEquals("expected", view.getText().toString()); 179 180 simpleCursorAdapter.setViewText(view, null); 181 assertEquals("", view.getText().toString()); 182 } 183 184 @UiThreadTest 185 public void testSetViewImage() { 186 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 187 // resId 188 int sceneryImgResId = com.android.cts.widget.R.drawable.scenery; 189 ImageView view = new ImageView(mContext); 190 assertNull(view.getDrawable()); 191 simpleCursorAdapter.setViewImage(view, String.valueOf(sceneryImgResId)); 192 assertNotNull(view.getDrawable()); 193 BitmapDrawable d = (BitmapDrawable) mContext.getResources().getDrawable( 194 sceneryImgResId); 195 WidgetTestUtils.assertEquals(d.getBitmap(), 196 ((BitmapDrawable) view.getDrawable()).getBitmap()); 197 198 // blank 199 view = new ImageView(mContext); 200 assertNull(view.getDrawable()); 201 simpleCursorAdapter.setViewImage(view, ""); 202 assertNull(view.getDrawable()); 203 204 // null 205 view = new ImageView(mContext); 206 assertNull(view.getDrawable()); 207 try { 208 // Should declare NullPoinertException if the uri or value is null 209 simpleCursorAdapter.setViewImage(view, null); 210 fail("Should throw NullPointerException if the uri or value is null"); 211 } catch (NullPointerException e) { 212 // expected 213 } 214 215 // uri 216 view = new ImageView(mContext); 217 assertNull(view.getDrawable()); 218 try { 219 int testimgRawId = com.android.cts.widget.R.raw.testimage; 220 simpleCursorAdapter.setViewImage(view, 221 createTestImage(mContext, SAMPLE_IMAGE_NAME, testimgRawId)); 222 assertNotNull(view.getDrawable()); 223 Bitmap actualBitmap = ((BitmapDrawable) view.getDrawable()).getBitmap(); 224 Bitmap testBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(mContext.getResources(), 225 testimgRawId, actualBitmap.getConfig()); 226 WidgetTestUtils.assertEquals(testBitmap, actualBitmap); 227 } finally { 228 destroyTestImage(mContext, SAMPLE_IMAGE_NAME); 229 } 230 } 231 232 @UiThreadTest 233 public void testAccessStringConversionColumn() { 234 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 235 // default is -1 236 assertEquals(-1, simpleCursorAdapter.getStringConversionColumn()); 237 238 simpleCursorAdapter.setStringConversionColumn(1); 239 assertEquals(1, simpleCursorAdapter.getStringConversionColumn()); 240 241 // Should check whether the column index is out of bounds 242 simpleCursorAdapter.setStringConversionColumn(Integer.MAX_VALUE); 243 assertEquals(Integer.MAX_VALUE, simpleCursorAdapter.getStringConversionColumn()); 244 245 // Should check whether the column index is out of bounds 246 simpleCursorAdapter.setStringConversionColumn(Integer.MIN_VALUE); 247 assertEquals(Integer.MIN_VALUE, simpleCursorAdapter.getStringConversionColumn()); 248 } 249 250 @UiThreadTest 251 public void testAccessCursorToStringConverter() { 252 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 253 // default is null 254 assertNull(simpleCursorAdapter.getCursorToStringConverter()); 255 256 CursorToStringConverter converter = new MockCursorToStringConverter(); 257 simpleCursorAdapter.setCursorToStringConverter(converter); 258 assertSame(converter, simpleCursorAdapter.getCursorToStringConverter()); 259 260 simpleCursorAdapter.setCursorToStringConverter(null); 261 assertNull(simpleCursorAdapter.getCursorToStringConverter()); 262 } 263 264 @UiThreadTest 265 public void testChangeCursor() { 266 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 267 // have "column1" 268 Cursor curWith3Columns = createTestCursor(3, ADAPTER_ROW_COUNT); 269 simpleCursorAdapter.changeCursor(curWith3Columns); 270 assertSame(curWith3Columns, simpleCursorAdapter.getCursor()); 271 272 // does not have "column1" 273 Cursor curWith1Column = createTestCursor(1, ADAPTER_ROW_COUNT); 274 try { 275 simpleCursorAdapter.changeCursor(curWith1Column); 276 fail("Should throw exception if the cursor does not have the " 277 + "original column passed in the constructor"); 278 } catch (IllegalArgumentException e) { 279 // expected 280 } 281 } 282 283 @UiThreadTest 284 public void testConvertToString() { 285 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 286 mCursor.moveToFirst(); 287 assertEquals("", simpleCursorAdapter.convertToString(null)); 288 289 // converter is null, StringConversionColumn is set to negative 290 simpleCursorAdapter.setStringConversionColumn(Integer.MIN_VALUE); 291 assertEquals(mCursor.toString(), simpleCursorAdapter.convertToString(mCursor)); 292 293 // converter is null, StringConversionColumn is set to 1 294 simpleCursorAdapter.setStringConversionColumn(1); 295 assertEquals("01", simpleCursorAdapter.convertToString(mCursor)); 296 297 // converter is null, StringConversionColumn is set to 3 (larger than columns count) 298 // the cursor has 3 columns including column0, column1 and _id which is added automatically 299 simpleCursorAdapter.setStringConversionColumn(DEFAULT_COLUMN_COUNT + 1); 300 try { 301 simpleCursorAdapter.convertToString(mCursor); 302 fail("Should throw IndexOutOfBoundsException if index is beyond the columns count"); 303 } catch (IndexOutOfBoundsException e) { 304 // expected 305 } 306 307 Cursor curWith3Columns = createTestCursor(DEFAULT_COLUMN_COUNT + 1, ADAPTER_ROW_COUNT); 308 curWith3Columns.moveToFirst(); 309 310 // converter is null, StringConversionColumn is set to 3 311 // and covert with a cursor which has 4 columns 312 simpleCursorAdapter.setStringConversionColumn(2); 313 assertEquals("02", simpleCursorAdapter.convertToString(curWith3Columns)); 314 315 // converter is not null, StringConversionColumn is 1 316 CursorToStringConverter converter = new MockCursorToStringConverter(); 317 simpleCursorAdapter.setCursorToStringConverter(converter); 318 simpleCursorAdapter.setStringConversionColumn(1); 319 ((MockCursorToStringConverter) converter).reset(); 320 simpleCursorAdapter.convertToString(curWith3Columns); 321 assertTrue(((MockCursorToStringConverter) converter).hasCalledConvertToString()); 322 } 323 324 @UiThreadTest 325 public void testNewView() { 326 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 327 LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService( 328 Context.LAYOUT_INFLATER_SERVICE); 329 ViewGroup viewGroup = (ViewGroup) layoutInflater.inflate( 330 com.android.cts.widget.R.layout.cursoradapter_host, null); 331 View result = simpleCursorAdapter.newView(mContext, null, viewGroup); 332 assertNotNull(result); 333 assertEquals(R.id.cursorAdapter_item0, result.getId()); 334 335 result = simpleCursorAdapter.newView(mContext, null, null); 336 assertNotNull(result); 337 assertEquals(R.id.cursorAdapter_item0, result.getId()); 338 } 339 340 @UiThreadTest 341 public void testNewDropDownView() { 342 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 343 LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService( 344 Context.LAYOUT_INFLATER_SERVICE); 345 ViewGroup viewGroup = (ViewGroup) layoutInflater.inflate( 346 com.android.cts.widget.R.layout.cursoradapter_host, null); 347 View result = simpleCursorAdapter.newDropDownView(null, null, viewGroup); 348 assertNotNull(result); 349 assertEquals(R.id.cursorAdapter_item0, result.getId()); 350 } 351 352 @UiThreadTest 353 public void testChangeCursorAndColumns() { 354 SimpleCursorAdapter simpleCursorAdapter = makeSimpleCursorAdapter(); 355 assertSame(mCursor, simpleCursorAdapter.getCursor()); 356 357 TextView listItem = (TextView) simpleCursorAdapter.newView(mContext, null, null); 358 359 mCursor.moveToFirst(); 360 simpleCursorAdapter.bindView(listItem, null, mCursor); 361 assertEquals("01", listItem.getText().toString()); 362 363 mCursor.moveToLast(); 364 simpleCursorAdapter.bindView(listItem, null, mCursor); 365 assertEquals("191", listItem.getText().toString()); 366 367 Cursor newCursor = createTestCursor(3, ADAPTER_ROW_COUNT); 368 final String[] from = new String[] { "column2" }; 369 simpleCursorAdapter.changeCursorAndColumns(newCursor, from, VIEWS_TO); 370 assertSame(newCursor, simpleCursorAdapter.getCursor()); 371 newCursor.moveToFirst(); 372 simpleCursorAdapter.bindView(listItem, null, newCursor); 373 assertEquals("02", listItem.getText().toString()); 374 375 newCursor.moveToLast(); 376 simpleCursorAdapter.bindView(listItem, null, newCursor); 377 assertEquals("192", listItem.getText().toString()); 378 379 simpleCursorAdapter.changeCursorAndColumns(null, null, null); 380 assertNull(simpleCursorAdapter.getCursor()); 381 } 382 383 /** 384 * Creates the test cursor. 385 * 386 * @param colCount the column count 387 * @param rowCount the row count 388 * @return the cursor 389 */ 390 @SuppressWarnings("unchecked") 391 private Cursor createTestCursor(int colCount, int rowCount) { 392 String[] columns = new String[colCount + 1]; 393 for (int i = 0; i < colCount; i++) { 394 columns[i] = "column" + i; 395 } 396 columns[colCount] = "_id"; 397 398 MatrixCursor cursor = new MatrixCursor(columns, rowCount); 399 Object[] row = new Object[colCount + 1]; 400 for (int i = 0; i < rowCount; i++) { 401 for (int j = 0; j < colCount; j++) { 402 row[j] = "" + i + "" + j; 403 } 404 row[colCount] = i; 405 cursor.addRow(row); 406 } 407 return cursor; 408 } 409 410 private static class MockViewBinder implements ViewBinder { 411 private boolean mExpectedResult; 412 413 private boolean mHasCalledSetViewValue; 414 415 public MockViewBinder(boolean expectedResult) { 416 mExpectedResult = expectedResult; 417 } 418 419 public void reset(){ 420 mHasCalledSetViewValue = false; 421 } 422 423 public boolean hasCalledSetViewValueCalledCount() { 424 return mHasCalledSetViewValue; 425 } 426 427 public boolean setViewValue(View view, Cursor cursor, int columnIndex) { 428 mHasCalledSetViewValue = true; 429 return mExpectedResult; 430 } 431 } 432 433 public static String createTestImage(Context context, String fileName, int resId) { 434 InputStream source = null; 435 OutputStream target = null; 436 437 try { 438 source = context.getResources().openRawResource(resId); 439 target = context.openFileOutput(fileName, Context.MODE_WORLD_WRITEABLE); 440 441 byte[] buffer = new byte[1024]; 442 for (int len = source.read(buffer); len > 0; len = source.read(buffer)) { 443 target.write(buffer, 0, len); 444 } 445 } catch (IOException e) { 446 fail(e.getMessage()); 447 } finally { 448 try { 449 if (source != null) { 450 source.close(); 451 } 452 if (target != null) { 453 target.close(); 454 } 455 } catch (IOException e) { 456 // Ignore the IOException. 457 } 458 } 459 460 return context.getFileStreamPath(fileName).getAbsolutePath(); 461 } 462 463 public static void destroyTestImage(Context context, String fileName) { 464 context.deleteFile(fileName); 465 } 466 467 private static class MockCursorToStringConverter implements CursorToStringConverter { 468 private boolean mHasCalledConvertToString; 469 470 public boolean hasCalledConvertToString() { 471 return mHasCalledConvertToString; 472 } 473 474 public void reset(){ 475 mHasCalledConvertToString = false; 476 } 477 478 public CharSequence convertToString(Cursor cursor) { 479 mHasCalledConvertToString = true; 480 return null; 481 } 482 } 483 } 484