1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.KITKAT; 4 import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 5 import static java.nio.charset.StandardCharsets.UTF_8; 6 import static org.assertj.core.api.Assertions.assertThat; 7 import static org.mockito.Matchers.same; 8 import static org.mockito.Mockito.doReturn; 9 import static org.mockito.Mockito.mock; 10 import static org.mockito.Mockito.verify; 11 import static org.robolectric.Shadows.shadowOf; 12 import static org.robolectric.annotation.Config.NONE; 13 14 import android.accounts.Account; 15 import android.app.Application; 16 import android.content.ContentProvider; 17 import android.content.ContentProviderOperation; 18 import android.content.ContentProviderResult; 19 import android.content.ContentResolver; 20 import android.content.ContentUris; 21 import android.content.ContentValues; 22 import android.content.Intent; 23 import android.content.OperationApplicationException; 24 import android.content.PeriodicSync; 25 import android.content.UriPermission; 26 import android.content.pm.ProviderInfo; 27 import android.content.res.AssetFileDescriptor; 28 import android.database.ContentObserver; 29 import android.database.Cursor; 30 import android.database.MatrixCursor; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.os.CancellationSignal; 34 import android.os.Handler; 35 import android.os.ParcelFileDescriptor; 36 import android.os.RemoteException; 37 import java.io.ByteArrayInputStream; 38 import java.io.File; 39 import java.io.FileDescriptor; 40 import java.io.FileNotFoundException; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.io.OutputStream; 44 import java.util.ArrayList; 45 import java.util.List; 46 import org.junit.Before; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.mockito.ArgumentCaptor; 50 import org.robolectric.Robolectric; 51 import org.robolectric.RobolectricTestRunner; 52 import org.robolectric.RuntimeEnvironment; 53 import org.robolectric.annotation.Config; 54 import org.robolectric.fakes.BaseCursor; 55 56 @RunWith(RobolectricTestRunner.class) 57 public class ShadowContentResolverTest { 58 private static final String AUTHORITY = "org.robolectric"; 59 60 private ContentResolver contentResolver; 61 private ShadowContentResolver shadowContentResolver; 62 private Uri uri21; 63 private Uri uri22; 64 private Account a, b; 65 66 @Before 67 public void setUp() { 68 contentResolver = RuntimeEnvironment.application.getContentResolver(); 69 shadowContentResolver = shadowOf(contentResolver); 70 uri21 = Uri.parse(EXTERNAL_CONTENT_URI.toString() + "/21"); 71 uri22 = Uri.parse(EXTERNAL_CONTENT_URI.toString() + "/22"); 72 73 a = new Account("a", "type"); 74 b = new Account("b", "type"); 75 } 76 77 @Test 78 public void insert_shouldReturnIncreasingUris() { 79 shadowContentResolver.setNextDatabaseIdForInserts(20); 80 81 assertThat(contentResolver.insert(EXTERNAL_CONTENT_URI, new ContentValues())).isEqualTo(uri21); 82 assertThat(contentResolver.insert(EXTERNAL_CONTENT_URI, new ContentValues())).isEqualTo(uri22); 83 } 84 85 @Test 86 public void getType_shouldDefaultToNull() { 87 assertThat(contentResolver.getType(uri21)).isNull(); 88 } 89 90 @Test 91 public void getType_shouldReturnProviderValue() { 92 ShadowContentResolver.registerProviderInternal(AUTHORITY, new ContentProvider() { 93 @Override public boolean onCreate() { 94 return false; 95 } 96 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 97 return new BaseCursor(); 98 } 99 @Override public Uri insert(Uri uri, ContentValues values) { 100 return null; 101 } 102 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { 103 return -1; 104 } 105 @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 106 return -1; 107 } 108 @Override public String getType(Uri uri) { 109 return "mytype"; 110 } 111 }); 112 final Uri uri = Uri.parse("content://"+AUTHORITY+"/some/path"); 113 assertThat(contentResolver.getType(uri)).isEqualTo("mytype"); 114 } 115 116 @Test 117 public void insert_shouldTrackInsertStatements() { 118 ContentValues contentValues = new ContentValues(); 119 contentValues.put("foo", "bar"); 120 contentResolver.insert(EXTERNAL_CONTENT_URI, contentValues); 121 assertThat(shadowContentResolver.getInsertStatements().size()).isEqualTo(1); 122 assertThat(shadowContentResolver.getInsertStatements().get(0).getUri()).isEqualTo(EXTERNAL_CONTENT_URI); 123 assertThat(shadowContentResolver.getInsertStatements().get(0).getContentValues().getAsString("foo")).isEqualTo("bar"); 124 125 contentValues = new ContentValues(); 126 contentValues.put("hello", "world"); 127 contentResolver.insert(EXTERNAL_CONTENT_URI, contentValues); 128 assertThat(shadowContentResolver.getInsertStatements().size()).isEqualTo(2); 129 assertThat(shadowContentResolver.getInsertStatements().get(1).getContentValues().getAsString("hello")).isEqualTo("world"); 130 } 131 132 @Test 133 public void insert_shouldTrackUpdateStatements() { 134 ContentValues contentValues = new ContentValues(); 135 contentValues.put("foo", "bar"); 136 contentResolver.update(EXTERNAL_CONTENT_URI, contentValues, "robolectric", new String[] { "awesome" }); 137 assertThat(shadowContentResolver.getUpdateStatements().size()).isEqualTo(1); 138 assertThat(shadowContentResolver.getUpdateStatements().get(0).getUri()).isEqualTo(EXTERNAL_CONTENT_URI); 139 assertThat(shadowContentResolver.getUpdateStatements().get(0).getContentValues().getAsString("foo")).isEqualTo("bar"); 140 assertThat(shadowContentResolver.getUpdateStatements().get(0).getWhere()).isEqualTo("robolectric"); 141 assertThat(shadowContentResolver.getUpdateStatements().get(0).getSelectionArgs()).isEqualTo(new String[]{"awesome"}); 142 143 contentValues = new ContentValues(); 144 contentValues.put("hello", "world"); 145 contentResolver.update(EXTERNAL_CONTENT_URI, contentValues, null, null); 146 assertThat(shadowContentResolver.getUpdateStatements().size()).isEqualTo(2); 147 assertThat(shadowContentResolver.getUpdateStatements().get(1).getUri()).isEqualTo(EXTERNAL_CONTENT_URI); 148 assertThat(shadowContentResolver.getUpdateStatements().get(1).getContentValues().getAsString("hello")).isEqualTo("world"); 149 assertThat(shadowContentResolver.getUpdateStatements().get(1).getWhere()).isNull(); 150 assertThat(shadowContentResolver.getUpdateStatements().get(1).getSelectionArgs()).isNull(); 151 } 152 153 @Test 154 public void insert_supportsNullContentValues() { 155 contentResolver.insert(EXTERNAL_CONTENT_URI, null); 156 assertThat(shadowContentResolver.getInsertStatements().get(0).getContentValues()).isNull(); 157 } 158 159 @Test 160 public void update_supportsNullContentValues() { 161 contentResolver.update(EXTERNAL_CONTENT_URI, null, null, null); 162 assertThat(shadowContentResolver.getUpdateStatements().get(0).getContentValues()).isNull(); 163 } 164 165 @Test 166 public void delete_shouldTrackDeletedUris() { 167 assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(0); 168 169 assertThat(contentResolver.delete(uri21, null, null)).isEqualTo(1); 170 assertThat(shadowContentResolver.getDeletedUris()).contains(uri21); 171 assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(1); 172 173 assertThat(contentResolver.delete(uri22, null, null)).isEqualTo(1); 174 assertThat(shadowContentResolver.getDeletedUris()).contains(uri22); 175 assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(2); 176 } 177 178 @Test 179 public void delete_shouldTrackDeletedStatements() { 180 assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(0); 181 182 assertThat(contentResolver.delete(uri21, "id", new String[]{"5"})).isEqualTo(1); 183 assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(1); 184 assertThat(shadowContentResolver.getDeleteStatements().get(0).getUri()).isEqualTo(uri21); 185 assertThat(shadowContentResolver.getDeleteStatements().get(0).getContentProvider()).isNull(); 186 assertThat(shadowContentResolver.getDeleteStatements().get(0).getWhere()).isEqualTo("id"); 187 assertThat(shadowContentResolver.getDeleteStatements().get(0).getSelectionArgs()[0]).isEqualTo("5"); 188 189 assertThat(contentResolver.delete(uri21, "foo", new String[]{"bar"})).isEqualTo(1); 190 assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(2); 191 assertThat(shadowContentResolver.getDeleteStatements().get(1).getUri()).isEqualTo(uri21); 192 assertThat(shadowContentResolver.getDeleteStatements().get(1).getWhere()).isEqualTo("foo"); 193 assertThat(shadowContentResolver.getDeleteStatements().get(1).getSelectionArgs()[0]).isEqualTo("bar"); 194 } 195 196 @Test 197 public void whenCursorHasBeenSet_query_shouldReturnTheCursor() { 198 assertThat(shadowContentResolver.query(null, null, null, null, null)).isNull(); 199 BaseCursor cursor = new BaseCursor(); 200 shadowContentResolver.setCursor(cursor); 201 assertThat((BaseCursor) shadowContentResolver.query(null, null, null, null, null)).isSameAs(cursor); 202 } 203 204 @Test 205 public void whenCursorHasBeenSet_queryWithCancellationSignal_shouldReturnTheCursor() { 206 assertThat(shadowContentResolver.query(null, null, null, null, null, new CancellationSignal())).isNull(); 207 BaseCursor cursor = new BaseCursor(); 208 shadowContentResolver.setCursor(cursor); 209 assertThat((BaseCursor) shadowContentResolver.query(null, null, null, null, null, new CancellationSignal())).isSameAs(cursor); 210 } 211 212 @Test 213 public void query_shouldReturnSpecificCursorsForSpecificUris() { 214 assertThat(shadowContentResolver.query(uri21, null, null, null, null)).isNull(); 215 assertThat(shadowContentResolver.query(uri22, null, null, null, null)).isNull(); 216 217 BaseCursor cursor21 = new BaseCursor(); 218 BaseCursor cursor22 = new BaseCursor(); 219 shadowContentResolver.setCursor(uri21, cursor21); 220 shadowContentResolver.setCursor(uri22, cursor22); 221 222 assertThat((BaseCursor) shadowContentResolver.query(uri21, null, null, null, null)).isSameAs(cursor21); 223 assertThat((BaseCursor) shadowContentResolver.query(uri22, null, null, null, null)).isSameAs(cursor22); 224 } 225 226 @Test 227 public void query_shouldKnowWhatItsParamsWere() { 228 String[] projection = {}; 229 String selection = "select"; 230 String[] selectionArgs = {}; 231 String sortOrder = "order"; 232 233 QueryParamTrackingCursor testCursor = new QueryParamTrackingCursor(); 234 235 shadowContentResolver.setCursor(testCursor); 236 Cursor cursor = shadowContentResolver.query(uri21, projection, selection, selectionArgs, sortOrder); 237 assertThat((QueryParamTrackingCursor) cursor).isEqualTo(testCursor); 238 assertThat(testCursor.uri).isEqualTo(uri21); 239 assertThat(testCursor.projection).isEqualTo(projection); 240 assertThat(testCursor.selection).isEqualTo(selection); 241 assertThat(testCursor.selectionArgs).isEqualTo(selectionArgs); 242 assertThat(testCursor.sortOrder).isEqualTo(sortOrder); 243 } 244 245 @Test 246 public void acquireUnstableProvider_shouldDefaultToNull() { 247 assertThat(contentResolver.acquireUnstableProvider(uri21)).isNull(); 248 } 249 250 @Test 251 public void acquireUnstableProvider_shouldReturnWithUri() { 252 ContentProvider cp = mock(ContentProvider.class); 253 ShadowContentResolver.registerProviderInternal(AUTHORITY, cp); 254 final Uri uri = Uri.parse("content://" + AUTHORITY); 255 assertThat(contentResolver.acquireUnstableProvider(uri)).isSameAs(cp.getIContentProvider()); 256 } 257 258 @Test 259 public void acquireUnstableProvider_shouldReturnWithString() { 260 ContentProvider cp = mock(ContentProvider.class); 261 ShadowContentResolver.registerProviderInternal(AUTHORITY, cp); 262 assertThat(contentResolver.acquireUnstableProvider(AUTHORITY)).isSameAs(cp.getIContentProvider()); 263 } 264 265 @Test 266 public void call_shouldCallProvider() { 267 final String METHOD = "method"; 268 final String ARG = "arg"; 269 final Bundle EXTRAS = new Bundle(); 270 final Uri uri = Uri.parse("content://" + AUTHORITY); 271 272 ContentProvider provider = mock(ContentProvider.class); 273 doReturn(null).when(provider).call(METHOD, ARG, EXTRAS); 274 ShadowContentResolver.registerProviderInternal(AUTHORITY, provider); 275 276 contentResolver.call(uri, METHOD, ARG, EXTRAS); 277 verify(provider).call(METHOD, ARG, EXTRAS); 278 } 279 280 @Test 281 public void registerProvider_shouldAttachProviderInfo() { 282 ContentProvider mock = mock(ContentProvider.class); 283 284 ProviderInfo providerInfo0 = new ProviderInfo(); 285 providerInfo0.authority = "the-authority"; // todo: support multiple authorities 286 providerInfo0.grantUriPermissions = true; 287 mock.attachInfo(RuntimeEnvironment.application, providerInfo0); 288 mock.onCreate(); 289 290 ArgumentCaptor<ProviderInfo> captor = ArgumentCaptor.forClass(ProviderInfo.class); 291 verify(mock).attachInfo(same(RuntimeEnvironment.application), captor.capture()); 292 ProviderInfo providerInfo = captor.getValue(); 293 294 assertThat(providerInfo.authority).isEqualTo("the-authority"); 295 assertThat(providerInfo.grantUriPermissions).isEqualTo(true); 296 } 297 298 @Test(expected = UnsupportedOperationException.class) 299 public void openInputStream_shouldReturnAnInputStreamThatExceptionsOnRead() throws Exception { 300 InputStream inputStream = contentResolver.openInputStream(uri21); 301 inputStream.read(); 302 } 303 304 @Test 305 public void openInputStream_returnsPreRegisteredStream() throws Exception { 306 shadowContentResolver.registerInputStream(uri21, new ByteArrayInputStream("ourStream".getBytes(UTF_8))); 307 InputStream inputStream = contentResolver.openInputStream(uri21); 308 byte[] data = new byte[9]; 309 inputStream.read(data); 310 assertThat(new String(data, UTF_8)).isEqualTo("ourStream"); 311 } 312 313 @Test 314 public void openOutputStream_shouldReturnAnOutputStream() throws Exception { 315 assertThat(contentResolver.openOutputStream(uri21)).isInstanceOf(OutputStream.class); 316 } 317 318 @Test 319 public void shouldTrackNotifiedUris() { 320 contentResolver.notifyChange(Uri.parse("foo"), null, true); 321 contentResolver.notifyChange(Uri.parse("bar"), null); 322 323 assertThat(shadowContentResolver.getNotifiedUris().size()).isEqualTo(2); 324 ShadowContentResolver.NotifiedUri uri = shadowContentResolver.getNotifiedUris().get(0); 325 326 assertThat(uri.uri.toString()).isEqualTo("foo"); 327 assertThat(uri.syncToNetwork).isTrue(); 328 assertThat(uri.observer).isNull(); 329 330 uri = shadowContentResolver.getNotifiedUris().get(1); 331 332 assertThat(uri.uri.toString()).isEqualTo("bar"); 333 assertThat(uri.syncToNetwork).isFalse(); 334 assertThat(uri.observer).isNull(); 335 } 336 337 @SuppressWarnings("serial") 338 @Test 339 public void applyBatchForRegisteredProvider() throws RemoteException, OperationApplicationException { 340 final List<String> operations = new ArrayList<>(); 341 ShadowContentResolver.registerProviderInternal("registeredProvider", new ContentProvider() { 342 @Override 343 public boolean onCreate() { 344 return true; 345 } 346 347 @Override 348 public Cursor query(Uri uri, String[] projection, String selection, 349 String[] selectionArgs, String sortOrder) { 350 operations.add("query"); 351 MatrixCursor cursor = new MatrixCursor(new String[] {"a"}); 352 cursor.addRow(new Object[] {"b"}); 353 return cursor; 354 } 355 356 @Override 357 public String getType(Uri uri) { 358 return null; 359 } 360 361 @Override 362 public Uri insert(Uri uri, ContentValues values) { 363 operations.add("insert"); 364 return ContentUris.withAppendedId(uri, 1); 365 } 366 367 @Override 368 public int delete(Uri uri, String selection, String[] selectionArgs) { 369 operations.add("delete"); 370 return 0; 371 } 372 373 @Override 374 public int update(Uri uri, ContentValues values, String selection, 375 String[] selectionArgs) { 376 operations.add("update"); 377 return 0; 378 } 379 380 }); 381 382 final Uri uri = Uri.parse("content://registeredProvider/path"); 383 contentResolver.applyBatch("registeredProvider", new ArrayList<ContentProviderOperation>() { 384 { 385 add(ContentProviderOperation.newInsert(uri).withValue("a", "b").build()); 386 add(ContentProviderOperation.newUpdate(uri).withValue("a", "b").build()); 387 add(ContentProviderOperation.newDelete(uri).build()); 388 add(ContentProviderOperation.newAssertQuery(uri).withValue("a", "b").build()); 389 } 390 }); 391 392 assertThat(operations).containsExactly("insert", "update", "delete", "query"); 393 } 394 395 @Test 396 public void applyBatchForUnregisteredProvider() throws RemoteException, OperationApplicationException { 397 List<ContentProviderOperation> resultOperations = shadowContentResolver.getContentProviderOperations(AUTHORITY); 398 assertThat(resultOperations).isNotNull(); 399 assertThat(resultOperations.size()).isEqualTo(0); 400 401 ContentProviderResult[] contentProviderResults = new ContentProviderResult[] { 402 new ContentProviderResult(1), 403 new ContentProviderResult(1), 404 }; 405 shadowContentResolver.setContentProviderResult(contentProviderResults); 406 Uri uri = Uri.parse("content://org.robolectric"); 407 ArrayList<ContentProviderOperation> operations = new ArrayList<>(); 408 operations.add(ContentProviderOperation.newInsert(uri) 409 .withValue("column1", "foo") 410 .withValue("column2", 5) 411 .build()); 412 operations.add(ContentProviderOperation.newUpdate(uri) 413 .withSelection("id_column", new String[] { "99" }) 414 .withValue("column1", "bar") 415 .build()); 416 operations.add(ContentProviderOperation.newDelete(uri) 417 .withSelection("id_column", new String[] { "11" }) 418 .build()); 419 ContentProviderResult[] result = contentResolver.applyBatch(AUTHORITY, operations); 420 421 resultOperations = shadowContentResolver.getContentProviderOperations(AUTHORITY); 422 assertThat(resultOperations).isEqualTo(operations); 423 assertThat(result).isEqualTo(contentProviderResults); 424 } 425 426 @Test 427 public void shouldKeepTrackOfSyncRequests() { 428 ShadowContentResolver.Status status = ShadowContentResolver.getStatus(a, AUTHORITY, true); 429 assertThat(status).isNotNull(); 430 assertThat(status.syncRequests).isEqualTo(0); 431 ContentResolver.requestSync(a, AUTHORITY, new Bundle()); 432 assertThat(status.syncRequests).isEqualTo(1); 433 assertThat(status.syncExtras).isNotNull(); 434 } 435 436 @Test 437 public void shouldKnowIfSyncIsActive() { 438 assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isFalse(); 439 ContentResolver.requestSync(a, AUTHORITY, new Bundle()); 440 assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isTrue(); 441 } 442 443 @Test 444 public void shouldCancelSync() { 445 ContentResolver.requestSync(a, AUTHORITY, new Bundle()); 446 ContentResolver.requestSync(b, AUTHORITY, new Bundle()); 447 assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isTrue(); 448 assertThat(ContentResolver.isSyncActive(b, AUTHORITY)).isTrue(); 449 450 ContentResolver.cancelSync(a, AUTHORITY); 451 assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isFalse(); 452 assertThat(ContentResolver.isSyncActive(b, AUTHORITY)).isTrue(); 453 } 454 455 @Test 456 public void shouldSetIsSyncable() { 457 assertThat(ContentResolver.getIsSyncable(a, AUTHORITY)).isEqualTo(-1); 458 assertThat(ContentResolver.getIsSyncable(b, AUTHORITY)).isEqualTo(-1); 459 ContentResolver.setIsSyncable(a, AUTHORITY, 1); 460 ContentResolver.setIsSyncable(b, AUTHORITY, 2); 461 assertThat(ContentResolver.getIsSyncable(a, AUTHORITY)).isEqualTo(1); 462 assertThat(ContentResolver.getIsSyncable(b, AUTHORITY)).isEqualTo(2); 463 } 464 465 @Test 466 public void shouldSetSyncAutomatically() { 467 assertThat(ContentResolver.getSyncAutomatically(a, AUTHORITY)).isFalse(); 468 ContentResolver.setSyncAutomatically(a, AUTHORITY, true); 469 assertThat(ContentResolver.getSyncAutomatically(a, AUTHORITY)).isTrue(); 470 } 471 472 @Test 473 public void shouldAddPeriodicSync() { 474 Bundle fooBar = new Bundle(); 475 fooBar.putString("foo", "bar"); 476 Bundle fooBaz = new Bundle(); 477 fooBaz.putString("foo", "baz"); 478 479 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 6000L); 480 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBaz, 6000L); 481 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBar, 6000L); 482 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 6000L); 483 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsOnly( 484 new PeriodicSync(a, AUTHORITY, fooBar, 6000L), 485 new PeriodicSync(a, AUTHORITY, fooBaz, 6000L)); 486 assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).containsOnly( 487 new PeriodicSync(b, AUTHORITY, fooBar, 6000L), 488 new PeriodicSync(b, AUTHORITY, fooBaz, 6000L)); 489 490 // If same extras, but different time, simply update the time. 491 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 42L); 492 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 42L); 493 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsOnly( 494 new PeriodicSync(a, AUTHORITY, fooBar, 42L), 495 new PeriodicSync(a, AUTHORITY, fooBaz, 6000L)); 496 assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).containsOnly( 497 new PeriodicSync(b, AUTHORITY, fooBar, 6000L), 498 new PeriodicSync(b, AUTHORITY, fooBaz, 42L)); 499 } 500 501 @Test 502 public void shouldRemovePeriodSync() { 503 Bundle fooBar = new Bundle(); 504 fooBar.putString("foo", "bar"); 505 Bundle fooBaz = new Bundle(); 506 fooBaz.putString("foo", "baz"); 507 Bundle foo42 = new Bundle(); 508 foo42.putInt("foo", 42); 509 assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).isEmpty(); 510 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).isEmpty(); 511 512 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 6000L); 513 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBaz, 6000L); 514 ContentResolver.addPeriodicSync(a, AUTHORITY, foo42, 6000L); 515 516 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBar, 6000L); 517 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 6000L); 518 ContentResolver.addPeriodicSync(b, AUTHORITY, foo42, 6000L); 519 520 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsOnly( 521 new PeriodicSync(a, AUTHORITY, fooBar, 6000L), 522 new PeriodicSync(a, AUTHORITY, fooBaz, 6000L), 523 new PeriodicSync(a, AUTHORITY, foo42, 6000L)); 524 525 ContentResolver.removePeriodicSync(a, AUTHORITY, fooBar); 526 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsOnly( 527 new PeriodicSync(a, AUTHORITY, fooBaz, 6000L), 528 new PeriodicSync(a, AUTHORITY, foo42, 6000L)); 529 530 ContentResolver.removePeriodicSync(a, AUTHORITY, fooBaz); 531 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsOnly( 532 new PeriodicSync(a, AUTHORITY, foo42, 6000L)); 533 534 ContentResolver.removePeriodicSync(a, AUTHORITY, foo42); 535 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).isEmpty(); 536 assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).containsOnly( 537 new PeriodicSync(b, AUTHORITY, fooBar, 6000L), 538 new PeriodicSync(b, AUTHORITY, fooBaz, 6000L), 539 new PeriodicSync(b, AUTHORITY, foo42, 6000L)); 540 } 541 542 @Test 543 public void shouldGetPeriodSyncs() { 544 assertThat(ContentResolver.getPeriodicSyncs(a, AUTHORITY).size()).isEqualTo(0); 545 ContentResolver.addPeriodicSync(a, AUTHORITY, new Bundle(), 6000L); 546 547 List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(a, AUTHORITY); 548 assertThat(syncs.size()).isEqualTo(1); 549 550 PeriodicSync first = syncs.get(0); 551 assertThat(first.account).isEqualTo(a); 552 assertThat(first.authority).isEqualTo(AUTHORITY); 553 assertThat(first.period).isEqualTo(6000L); 554 assertThat(first.extras).isNotNull(); 555 } 556 557 @Test 558 public void shouldValidateSyncExtras() { 559 Bundle bundle = new Bundle(); 560 bundle.putString("foo", "strings"); 561 bundle.putLong("long", 10L); 562 bundle.putDouble("double", 10.0d); 563 bundle.putFloat("float", 10.0f); 564 bundle.putInt("int", 10); 565 bundle.putParcelable("account", a); 566 ContentResolver.validateSyncExtrasBundle(bundle); 567 } 568 569 @Test(expected = IllegalArgumentException.class) 570 public void shouldValidateSyncExtrasAndThrow() { 571 Bundle bundle = new Bundle(); 572 bundle.putParcelable("intent", new Intent()); 573 ContentResolver.validateSyncExtrasBundle(bundle); 574 } 575 576 @Test 577 public void shouldSetMasterSyncAutomatically() { 578 assertThat(ContentResolver.getMasterSyncAutomatically()).isFalse(); 579 ContentResolver.setMasterSyncAutomatically(true); 580 assertThat(ContentResolver.getMasterSyncAutomatically()).isTrue(); 581 } 582 583 @Test 584 public void shouldDelegateCallsToRegisteredProvider() { 585 ShadowContentResolver.registerProviderInternal(AUTHORITY, new ContentProvider() { 586 @Override 587 public boolean onCreate() { 588 return false; 589 } 590 591 @Override 592 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 593 return new BaseCursor(); 594 } 595 596 @Override 597 public Uri insert(Uri uri, ContentValues values) { 598 return null; 599 } 600 601 @Override 602 public int delete(Uri uri, String selection, String[] selectionArgs) { 603 return -1; 604 } 605 606 @Override 607 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 608 return -1; 609 } 610 611 @Override 612 public String getType(Uri uri) { 613 return null; 614 } 615 }); 616 final Uri uri = Uri.parse("content://"+AUTHORITY+"/some/path"); 617 final Uri unrelated = Uri.parse("content://unrelated/some/path"); 618 619 assertThat(contentResolver.query(uri, null, null, null, null)).isNotNull(); 620 assertThat(contentResolver.insert(uri, new ContentValues())).isNull(); 621 622 assertThat(contentResolver.delete(uri, null, null)).isEqualTo(-1); 623 assertThat(contentResolver.update(uri, new ContentValues(), null, null)).isEqualTo(-1); 624 625 assertThat(contentResolver.query(unrelated, null, null, null, null)).isNull(); 626 assertThat(contentResolver.insert(unrelated, new ContentValues())).isNotNull(); 627 assertThat(contentResolver.delete(unrelated, null, null)).isEqualTo(1); 628 assertThat(contentResolver.update(unrelated, new ContentValues(), null, null)).isEqualTo(1); 629 } 630 631 @Test 632 public void shouldRegisterContentObservers() { 633 TestContentObserver co = new TestContentObserver(null); 634 ShadowContentResolver scr = shadowOf(contentResolver); 635 636 assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).isEmpty(); 637 638 contentResolver.registerContentObserver(EXTERNAL_CONTENT_URI, true, co); 639 640 assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).containsExactly((ContentObserver) co); 641 642 assertThat(co.changed).isFalse(); 643 contentResolver.notifyChange(EXTERNAL_CONTENT_URI, null); 644 assertThat(co.changed).isTrue(); 645 646 scr.clearContentObservers(); 647 assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).isEmpty(); 648 } 649 650 @Test 651 public void shouldUnregisterContentObservers() { 652 TestContentObserver co = new TestContentObserver(null); 653 ShadowContentResolver scr = shadowOf(contentResolver); 654 contentResolver.registerContentObserver(EXTERNAL_CONTENT_URI, true, co); 655 assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)) 656 .containsExactlyInAnyOrder((ContentObserver) co); 657 658 contentResolver.unregisterContentObserver(co); 659 assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).isEmpty(); 660 661 assertThat(co.changed).isFalse(); 662 contentResolver.notifyChange(EXTERNAL_CONTENT_URI, null); 663 assertThat(co.changed).isFalse(); 664 } 665 666 @Test 667 public void shouldNotifyChildContentObservers() throws Exception { 668 TestContentObserver co1 = new TestContentObserver(null); 669 TestContentObserver co2 = new TestContentObserver(null); 670 671 Uri childUri = EXTERNAL_CONTENT_URI.buildUpon().appendPath("path").build(); 672 673 contentResolver.registerContentObserver(EXTERNAL_CONTENT_URI, true, co1); 674 contentResolver.registerContentObserver(childUri, false, co2); 675 676 co1.changed = co2.changed = false; 677 contentResolver.notifyChange(childUri, null); 678 assertThat(co1.changed).isTrue(); 679 assertThat(co2.changed).isTrue(); 680 681 co1.changed = co2.changed = false; 682 contentResolver.notifyChange(EXTERNAL_CONTENT_URI, null); 683 assertThat(co1.changed).isTrue(); 684 assertThat(co2.changed).isFalse(); 685 686 co1.changed = co2.changed = false; 687 contentResolver.notifyChange(childUri.buildUpon().appendPath("extra").build(), null); 688 assertThat(co1.changed).isTrue(); 689 assertThat(co2.changed).isFalse(); 690 } 691 692 @Test 693 public void getProvider_shouldCreateProviderFromManifest() throws Exception { 694 Uri uri = Uri.parse("content://org.robolectric.my_content_provider_authority/shadows"); 695 ContentProvider provider = ShadowContentResolver.getProvider(uri); 696 assertThat(provider).isNotNull(); 697 assertThat(provider.getReadPermission()).isEqualTo("READ_PERMISSION"); 698 assertThat(provider.getWritePermission()).isEqualTo("WRITE_PERMISSION"); 699 assertThat(provider.getPathPermissions()).hasSize(1); 700 701 // unfortunately, there is no direct way of testing if authority is set or not 702 // however, it's checked in ContentProvider.Transport method calls (validateIncomingUri), so 703 // it's the closest we can test against 704 provider.getIContentProvider().getType(uri); // should not throw 705 } 706 707 @Test 708 @Config(manifest = NONE) 709 public void getProvider_shouldNotReturnAnyProviderWhenManifestIsNull() { 710 Application application = new Application(); 711 shadowOf(application).callAttach(RuntimeEnvironment.systemContext); 712 assertThat(ShadowContentResolver.getProvider(Uri.parse("content://"))).isNull(); 713 } 714 715 716 717 @Test 718 public void openTypedAssetFileDescriptor_shouldOpenDescriptor() throws IOException, RemoteException { 719 Robolectric.setupContentProvider(MyContentProvider.class, AUTHORITY); 720 721 AssetFileDescriptor afd = contentResolver.openTypedAssetFileDescriptor(Uri.parse("content://" + AUTHORITY + "/whatever"), "*/*", null); 722 723 FileDescriptor descriptor = afd.getFileDescriptor(); 724 assertThat(descriptor).isNotNull(); 725 } 726 727 @Test 728 @Config(minSdk = KITKAT) 729 public void takeAndReleasePersistableUriPermissions() { 730 List<UriPermission> permissions = contentResolver.getPersistedUriPermissions(); 731 assertThat(permissions).isEmpty(); 732 733 // Take the read permission for the uri. 734 Uri uri = Uri.parse("content://" + AUTHORITY + "/whatever"); 735 contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 736 assertThat(permissions).hasSize(1); 737 assertThat(permissions.get(0).getUri()).isSameAs(uri); 738 assertThat(permissions.get(0).isReadPermission()).isTrue(); 739 assertThat(permissions.get(0).isWritePermission()).isFalse(); 740 741 // Take the write permission for the uri. 742 contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 743 assertThat(permissions).hasSize(1); 744 assertThat(permissions.get(0).getUri()).isSameAs(uri); 745 assertThat(permissions.get(0).isReadPermission()).isTrue(); 746 assertThat(permissions.get(0).isWritePermission()).isTrue(); 747 748 // Release the read permission for the uri. 749 contentResolver.releasePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 750 assertThat(permissions).hasSize(1); 751 assertThat(permissions.get(0).getUri()).isSameAs(uri); 752 assertThat(permissions.get(0).isReadPermission()).isFalse(); 753 assertThat(permissions.get(0).isWritePermission()).isTrue(); 754 755 // Release the write permission for the uri. 756 contentResolver.releasePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 757 assertThat(permissions).isEmpty(); 758 } 759 760 private static class QueryParamTrackingCursor extends BaseCursor { 761 public Uri uri; 762 public String[] projection; 763 public String selection; 764 public String[] selectionArgs; 765 public String sortOrder; 766 767 @Override 768 public void setQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 769 this.uri = uri; 770 this.projection = projection; 771 this.selection = selection; 772 this.selectionArgs = selectionArgs; 773 this.sortOrder = sortOrder; 774 } 775 } 776 777 private static class TestContentObserver extends ContentObserver { 778 public TestContentObserver(Handler handler) { 779 super(handler); 780 } 781 782 public boolean changed = false; 783 784 @Override 785 public void onChange(boolean selfChange) { 786 changed = true; 787 } 788 789 @Override 790 public void onChange(boolean selfChange, Uri uri) { 791 changed = true; 792 } 793 } 794 795 public static class TestContentProvider extends ContentProvider { 796 @Override 797 public int delete(Uri arg0, String arg1, String[] arg2) { 798 return 0; 799 } 800 801 @Override 802 public String getType(Uri arg0) { 803 return null; 804 } 805 806 @Override 807 public Uri insert(Uri arg0, ContentValues arg1) { 808 return null; 809 } 810 811 @Override 812 public boolean onCreate() { 813 return false; 814 } 815 816 @Override 817 public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) { 818 return null; 819 } 820 821 @Override 822 public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) { 823 return 0; 824 } 825 } 826 827 /** 828 * Provider that opens a temporary file. 829 */ 830 public static class MyContentProvider extends ContentProvider { 831 @Override 832 public boolean onCreate() { 833 return true; 834 } 835 836 @Override 837 public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) { 838 return null; 839 } 840 841 @Override 842 public String getType(Uri uri) { 843 return null; 844 } 845 846 @Override 847 public Uri insert(Uri uri, ContentValues contentValues) { 848 return null; 849 } 850 851 @Override 852 public int delete(Uri uri, String s, String[] strings) { 853 return 0; 854 } 855 856 @Override 857 public int update(Uri uri, ContentValues contentValues, String s, String[] strings) { 858 return 0; 859 } 860 861 @Override 862 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 863 final File file = new File(RuntimeEnvironment.application.getFilesDir(), "test_file"); 864 try { 865 file.createNewFile(); 866 } catch (IOException e) { 867 throw new RuntimeException("error creating new file", e); 868 } 869 return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 870 } 871 } 872 873 } 874