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