1 package org.robolectric.shadows; 2 3 import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION; 4 import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS; 5 import static android.content.ContentResolver.QUERY_ARG_SQL_SORT_ORDER; 6 import static android.os.Build.VERSION_CODES.N; 7 import static android.os.Build.VERSION_CODES.O; 8 import static com.google.common.truth.Truth.assertThat; 9 import static java.nio.charset.StandardCharsets.UTF_8; 10 import static org.junit.Assert.assertThrows; 11 import static org.junit.Assert.fail; 12 import static org.mockito.ArgumentMatchers.same; 13 import static org.mockito.Mockito.doReturn; 14 import static org.mockito.Mockito.mock; 15 import static org.mockito.Mockito.verify; 16 import static org.robolectric.Shadows.shadowOf; 17 import static org.robolectric.annotation.Config.NONE; 18 19 import android.accounts.Account; 20 import android.annotation.SuppressLint; 21 import android.app.Application; 22 import android.content.ContentProvider; 23 import android.content.ContentProviderOperation; 24 import android.content.ContentProviderResult; 25 import android.content.ContentResolver; 26 import android.content.ContentUris; 27 import android.content.ContentValues; 28 import android.content.Intent; 29 import android.content.OperationApplicationException; 30 import android.content.PeriodicSync; 31 import android.content.SyncAdapterType; 32 import android.content.SyncInfo; 33 import android.content.UriPermission; 34 import android.content.pm.ProviderInfo; 35 import android.content.res.AssetFileDescriptor; 36 import android.database.ContentObserver; 37 import android.database.Cursor; 38 import android.database.MatrixCursor; 39 import android.net.Uri; 40 import android.os.Bundle; 41 import android.os.CancellationSignal; 42 import android.os.Handler; 43 import android.os.ParcelFileDescriptor; 44 import android.os.RemoteException; 45 import androidx.test.core.app.ApplicationProvider; 46 import androidx.test.ext.junit.runners.AndroidJUnit4; 47 import com.google.common.collect.Iterables; 48 import java.io.ByteArrayInputStream; 49 import java.io.File; 50 import java.io.FileDescriptor; 51 import java.io.FileNotFoundException; 52 import java.io.FileOutputStream; 53 import java.io.IOException; 54 import java.io.InputStream; 55 import java.io.OutputStream; 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.List; 59 import java.util.concurrent.atomic.AtomicBoolean; 60 import java.util.concurrent.atomic.AtomicInteger; 61 import org.junit.Before; 62 import org.junit.Rule; 63 import org.junit.Test; 64 import org.junit.rules.TemporaryFolder; 65 import org.junit.runner.RunWith; 66 import org.mockito.ArgumentCaptor; 67 import org.robolectric.R; 68 import org.robolectric.Robolectric; 69 import org.robolectric.RuntimeEnvironment; 70 import org.robolectric.annotation.Config; 71 import org.robolectric.fakes.BaseCursor; 72 import org.robolectric.util.NamedStream; 73 74 @RunWith(AndroidJUnit4.class) 75 public class ShadowContentResolverTest { 76 @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); 77 78 private static final Uri FAKE_URI = 79 Uri.parse("content://robolectric").buildUpon().appendPath("fakepath").build(); 80 81 private static final String AUTHORITY = "org.robolectric"; 82 83 private ContentResolver contentResolver; 84 private ShadowContentResolver shadowContentResolver; 85 private Uri uri21; 86 private Uri uri22; 87 private Account a, b; 88 89 @Before setUp()90 public void setUp() { 91 contentResolver = ApplicationProvider.getApplicationContext().getContentResolver(); 92 shadowContentResolver = shadowOf(contentResolver); 93 uri21 = Uri.parse(FAKE_URI.toString() + "/21"); 94 uri22 = Uri.parse(FAKE_URI.toString() + "/22"); 95 96 a = new Account("a", "type"); 97 b = new Account("b", "type"); 98 } 99 100 @Test insert_shouldReturnIncreasingUris()101 public void insert_shouldReturnIncreasingUris() { 102 shadowContentResolver.setNextDatabaseIdForInserts(20); 103 104 assertThat(contentResolver.insert(FAKE_URI, new ContentValues())).isEqualTo(uri21); 105 assertThat(contentResolver.insert(FAKE_URI, new ContentValues())).isEqualTo(uri22); 106 } 107 108 @Test getType_shouldDefaultToNull()109 public void getType_shouldDefaultToNull() { 110 assertThat(contentResolver.getType(uri21)).isNull(); 111 } 112 113 @Test getType_shouldReturnProviderValue()114 public void getType_shouldReturnProviderValue() { 115 ShadowContentResolver.registerProviderInternal( 116 AUTHORITY, 117 new ContentProvider() { 118 @Override 119 public boolean onCreate() { 120 return false; 121 } 122 123 @Override 124 public Cursor query( 125 Uri uri, 126 String[] projection, 127 String selection, 128 String[] selectionArgs, 129 String sortOrder) { 130 return new BaseCursor(); 131 } 132 133 @Override 134 public Uri insert(Uri uri, ContentValues values) { 135 return null; 136 } 137 138 @Override 139 public int delete(Uri uri, String selection, String[] selectionArgs) { 140 return -1; 141 } 142 143 @Override 144 public int update( 145 Uri uri, ContentValues values, String selection, String[] selectionArgs) { 146 return -1; 147 } 148 149 @Override 150 public String getType(Uri uri) { 151 return "mytype"; 152 } 153 }); 154 final Uri uri = Uri.parse("content://" + AUTHORITY + "/some/path"); 155 assertThat(contentResolver.getType(uri)).isEqualTo("mytype"); 156 } 157 158 @Test insert_shouldTrackInsertStatements()159 public void insert_shouldTrackInsertStatements() { 160 ContentValues contentValues = new ContentValues(); 161 contentValues.put("foo", "bar"); 162 contentResolver.insert(FAKE_URI, contentValues); 163 assertThat(shadowContentResolver.getInsertStatements().size()).isEqualTo(1); 164 assertThat(shadowContentResolver.getInsertStatements().get(0).getUri()).isEqualTo(FAKE_URI); 165 assertThat( 166 shadowContentResolver 167 .getInsertStatements() 168 .get(0) 169 .getContentValues() 170 .getAsString("foo")) 171 .isEqualTo("bar"); 172 173 contentValues = new ContentValues(); 174 contentValues.put("hello", "world"); 175 contentResolver.insert(FAKE_URI, contentValues); 176 assertThat(shadowContentResolver.getInsertStatements().size()).isEqualTo(2); 177 assertThat( 178 shadowContentResolver 179 .getInsertStatements() 180 .get(1) 181 .getContentValues() 182 .getAsString("hello")) 183 .isEqualTo("world"); 184 } 185 186 @Test insert_shouldTrackUpdateStatements()187 public void insert_shouldTrackUpdateStatements() { 188 ContentValues contentValues = new ContentValues(); 189 contentValues.put("foo", "bar"); 190 contentResolver.update(FAKE_URI, contentValues, "robolectric", new String[] {"awesome"}); 191 assertThat(shadowContentResolver.getUpdateStatements().size()).isEqualTo(1); 192 assertThat(shadowContentResolver.getUpdateStatements().get(0).getUri()).isEqualTo(FAKE_URI); 193 assertThat( 194 shadowContentResolver 195 .getUpdateStatements() 196 .get(0) 197 .getContentValues() 198 .getAsString("foo")) 199 .isEqualTo("bar"); 200 assertThat(shadowContentResolver.getUpdateStatements().get(0).getWhere()) 201 .isEqualTo("robolectric"); 202 assertThat(shadowContentResolver.getUpdateStatements().get(0).getSelectionArgs()) 203 .isEqualTo(new String[] {"awesome"}); 204 205 contentValues = new ContentValues(); 206 contentValues.put("hello", "world"); 207 contentResolver.update(FAKE_URI, contentValues, null, null); 208 assertThat(shadowContentResolver.getUpdateStatements().size()).isEqualTo(2); 209 assertThat(shadowContentResolver.getUpdateStatements().get(1).getUri()).isEqualTo(FAKE_URI); 210 assertThat( 211 shadowContentResolver 212 .getUpdateStatements() 213 .get(1) 214 .getContentValues() 215 .getAsString("hello")) 216 .isEqualTo("world"); 217 assertThat(shadowContentResolver.getUpdateStatements().get(1).getWhere()).isNull(); 218 assertThat(shadowContentResolver.getUpdateStatements().get(1).getSelectionArgs()).isNull(); 219 } 220 221 @Test insert_supportsNullContentValues()222 public void insert_supportsNullContentValues() { 223 contentResolver.insert(FAKE_URI, null); 224 assertThat(shadowContentResolver.getInsertStatements().get(0).getContentValues()).isNull(); 225 } 226 227 @Test update_supportsNullContentValues()228 public void update_supportsNullContentValues() { 229 contentResolver.update(FAKE_URI, null, null, null); 230 assertThat(shadowContentResolver.getUpdateStatements().get(0).getContentValues()).isNull(); 231 } 232 233 @Test delete_shouldTrackDeletedUris()234 public void delete_shouldTrackDeletedUris() { 235 assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(0); 236 237 assertThat(contentResolver.delete(uri21, null, null)).isEqualTo(1); 238 assertThat(shadowContentResolver.getDeletedUris()).contains(uri21); 239 assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(1); 240 241 assertThat(contentResolver.delete(uri22, null, null)).isEqualTo(1); 242 assertThat(shadowContentResolver.getDeletedUris()).contains(uri22); 243 assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(2); 244 } 245 246 @Test delete_shouldTrackDeletedStatements()247 public void delete_shouldTrackDeletedStatements() { 248 assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(0); 249 250 assertThat(contentResolver.delete(uri21, "id", new String[] {"5"})).isEqualTo(1); 251 assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(1); 252 assertThat(shadowContentResolver.getDeleteStatements().get(0).getUri()).isEqualTo(uri21); 253 assertThat(shadowContentResolver.getDeleteStatements().get(0).getContentProvider()).isNull(); 254 assertThat(shadowContentResolver.getDeleteStatements().get(0).getWhere()).isEqualTo("id"); 255 assertThat(shadowContentResolver.getDeleteStatements().get(0).getSelectionArgs()[0]) 256 .isEqualTo("5"); 257 258 assertThat(contentResolver.delete(uri21, "foo", new String[] {"bar"})).isEqualTo(1); 259 assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(2); 260 assertThat(shadowContentResolver.getDeleteStatements().get(1).getUri()).isEqualTo(uri21); 261 assertThat(shadowContentResolver.getDeleteStatements().get(1).getWhere()).isEqualTo("foo"); 262 assertThat(shadowContentResolver.getDeleteStatements().get(1).getSelectionArgs()[0]) 263 .isEqualTo("bar"); 264 } 265 266 @Test whenCursorHasBeenSet_query_shouldReturnTheCursor()267 public void whenCursorHasBeenSet_query_shouldReturnTheCursor() { 268 assertThat(shadowContentResolver.query(null, null, null, null, null)).isNull(); 269 BaseCursor cursor = new BaseCursor(); 270 shadowContentResolver.setCursor(cursor); 271 assertThat((BaseCursor) shadowContentResolver.query(null, null, null, null, null)) 272 .isSameInstanceAs(cursor); 273 } 274 275 @Test whenCursorHasBeenSet_queryWithCancellationSignal_shouldReturnTheCursor()276 public void whenCursorHasBeenSet_queryWithCancellationSignal_shouldReturnTheCursor() { 277 assertThat(shadowContentResolver.query(null, null, null, null, null, new CancellationSignal())) 278 .isNull(); 279 BaseCursor cursor = new BaseCursor(); 280 shadowContentResolver.setCursor(cursor); 281 assertThat( 282 (BaseCursor) 283 shadowContentResolver.query(null, null, null, null, null, new CancellationSignal())) 284 .isSameInstanceAs(cursor); 285 } 286 287 @Test query_shouldReturnSpecificCursorsForSpecificUris()288 public void query_shouldReturnSpecificCursorsForSpecificUris() { 289 assertThat(shadowContentResolver.query(uri21, null, null, null, null)).isNull(); 290 assertThat(shadowContentResolver.query(uri22, null, null, null, null)).isNull(); 291 292 BaseCursor cursor21 = new BaseCursor(); 293 BaseCursor cursor22 = new BaseCursor(); 294 shadowContentResolver.setCursor(uri21, cursor21); 295 shadowContentResolver.setCursor(uri22, cursor22); 296 297 assertThat((BaseCursor) shadowContentResolver.query(uri21, null, null, null, null)) 298 .isSameInstanceAs(cursor21); 299 assertThat((BaseCursor) shadowContentResolver.query(uri22, null, null, null, null)) 300 .isSameInstanceAs(cursor22); 301 } 302 303 @Test query_shouldKnowWhatItsParamsWere()304 public void query_shouldKnowWhatItsParamsWere() { 305 String[] projection = {}; 306 String selection = "select"; 307 String[] selectionArgs = {}; 308 String sortOrder = "order"; 309 310 QueryParamTrackingCursor testCursor = new QueryParamTrackingCursor(); 311 312 shadowContentResolver.setCursor(testCursor); 313 Cursor cursor = 314 shadowContentResolver.query(uri21, projection, selection, selectionArgs, sortOrder); 315 assertThat((QueryParamTrackingCursor) cursor).isEqualTo(testCursor); 316 assertThat(testCursor.uri).isEqualTo(uri21); 317 assertThat(testCursor.projection).isEqualTo(projection); 318 assertThat(testCursor.selection).isEqualTo(selection); 319 assertThat(testCursor.selectionArgs).isEqualTo(selectionArgs); 320 assertThat(testCursor.sortOrder).isEqualTo(sortOrder); 321 } 322 323 @Test 324 @Config(minSdk = O) query_shouldKnowWhatIsInBundle()325 public void query_shouldKnowWhatIsInBundle() { 326 String[] projection = {}; 327 String selection = "select"; 328 String[] selectionArgs = {}; 329 String sortOrder = "order"; 330 Bundle queryArgs = new Bundle(); 331 queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection); 332 queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs); 333 queryArgs.putString(QUERY_ARG_SQL_SORT_ORDER, sortOrder); 334 335 QueryParamTrackingCursor testCursor = new QueryParamTrackingCursor(); 336 shadowContentResolver.setCursor(testCursor); 337 Cursor cursor = shadowContentResolver.query(uri21, projection, queryArgs, null); 338 assertThat((QueryParamTrackingCursor) cursor).isEqualTo(testCursor); 339 assertThat(testCursor.uri).isEqualTo(uri21); 340 assertThat(testCursor.projection).isEqualTo(projection); 341 assertThat(testCursor.selection).isEqualTo(selection); 342 assertThat(testCursor.selectionArgs).isEqualTo(selectionArgs); 343 assertThat(testCursor.sortOrder).isEqualTo(sortOrder); 344 } 345 346 @Test acquireUnstableProvider_shouldDefaultToNull()347 public void acquireUnstableProvider_shouldDefaultToNull() { 348 assertThat(contentResolver.acquireUnstableProvider(uri21)).isNull(); 349 } 350 351 @Test acquireUnstableProvider_shouldReturnWithUri()352 public void acquireUnstableProvider_shouldReturnWithUri() { 353 ContentProvider cp = mock(ContentProvider.class); 354 ShadowContentResolver.registerProviderInternal(AUTHORITY, cp); 355 final Uri uri = Uri.parse("content://" + AUTHORITY); 356 assertThat(contentResolver.acquireUnstableProvider(uri)) 357 .isSameInstanceAs(cp.getIContentProvider()); 358 } 359 360 @Test acquireUnstableProvider_shouldReturnWithString()361 public void acquireUnstableProvider_shouldReturnWithString() { 362 ContentProvider cp = mock(ContentProvider.class); 363 ShadowContentResolver.registerProviderInternal(AUTHORITY, cp); 364 assertThat(contentResolver.acquireUnstableProvider(AUTHORITY)) 365 .isSameInstanceAs(cp.getIContentProvider()); 366 } 367 368 @Test call_shouldCallProvider()369 public void call_shouldCallProvider() { 370 final String METHOD = "method"; 371 final String ARG = "arg"; 372 final Bundle EXTRAS = new Bundle(); 373 final Uri uri = Uri.parse("content://" + AUTHORITY); 374 375 ContentProvider provider = mock(ContentProvider.class); 376 doReturn(null).when(provider).call(METHOD, ARG, EXTRAS); 377 ShadowContentResolver.registerProviderInternal(AUTHORITY, provider); 378 379 contentResolver.call(uri, METHOD, ARG, EXTRAS); 380 verify(provider).call(METHOD, ARG, EXTRAS); 381 } 382 383 @Test registerProvider_shouldAttachProviderInfo()384 public void registerProvider_shouldAttachProviderInfo() { 385 ContentProvider mock = mock(ContentProvider.class); 386 387 ProviderInfo providerInfo0 = new ProviderInfo(); 388 providerInfo0.authority = "the-authority"; // todo: support multiple authorities 389 providerInfo0.grantUriPermissions = true; 390 mock.attachInfo(ApplicationProvider.getApplicationContext(), providerInfo0); 391 mock.onCreate(); 392 393 ArgumentCaptor<ProviderInfo> captor = ArgumentCaptor.forClass(ProviderInfo.class); 394 verify(mock) 395 .attachInfo( 396 same((Application) ApplicationProvider.getApplicationContext()), captor.capture()); 397 ProviderInfo providerInfo = captor.getValue(); 398 399 assertThat(providerInfo.authority).isEqualTo("the-authority"); 400 assertThat(providerInfo.grantUriPermissions).isEqualTo(true); 401 } 402 403 @Test(expected = UnsupportedOperationException.class) openInputStream_shouldReturnAnInputStreamThatExceptionsOnRead()404 public void openInputStream_shouldReturnAnInputStreamThatExceptionsOnRead() throws Exception { 405 InputStream inputStream = contentResolver.openInputStream(uri21); 406 inputStream.read(); 407 } 408 409 @Test openInputStream_returnsPreRegisteredStream()410 public void openInputStream_returnsPreRegisteredStream() throws Exception { 411 shadowContentResolver.registerInputStream( 412 uri21, new ByteArrayInputStream("ourStream".getBytes(UTF_8))); 413 InputStream inputStream = contentResolver.openInputStream(uri21); 414 byte[] data = new byte[9]; 415 inputStream.read(data); 416 assertThat(new String(data, UTF_8)).isEqualTo("ourStream"); 417 } 418 419 @Test openInputStream_returnsNewStreamEachTimeFromRegisteredSupplier()420 public void openInputStream_returnsNewStreamEachTimeFromRegisteredSupplier() throws Exception { 421 shadowContentResolver.registerInputStreamSupplier( 422 uri21, () -> new ByteArrayInputStream("ourStream".getBytes(UTF_8))); 423 InputStream inputStream1 = contentResolver.openInputStream(uri21); 424 byte[] data1 = new byte[9]; 425 inputStream1.read(data1); 426 inputStream1.close(); 427 InputStream inputStream2 = contentResolver.openInputStream(uri21); 428 byte[] data2 = new byte[9]; 429 inputStream2.read(data2); 430 inputStream2.close(); 431 assertThat(new String(data1, UTF_8)).isEqualTo("ourStream"); 432 assertThat(new String(data2, UTF_8)).isEqualTo("ourStream"); 433 } 434 435 @Test openInputStream_returnsResourceUriStream()436 public void openInputStream_returnsResourceUriStream() throws Exception { 437 InputStream inputStream = 438 contentResolver.openInputStream( 439 new Uri.Builder() 440 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 441 .authority(ApplicationProvider.getApplicationContext().getPackageName()) 442 .appendPath(String.valueOf(R.drawable.an_image)) 443 .build()); 444 assertThat(inputStream).isNotNull(); 445 inputStream.read(); 446 } 447 448 @SuppressLint("NewApi") 449 @Test openInputStream_returnsFileUriStream()450 public void openInputStream_returnsFileUriStream() throws Exception { 451 File file = temporaryFolder.newFile(); 452 try (FileOutputStream out = new FileOutputStream(file)) { 453 out.write("foo".getBytes(UTF_8)); 454 } 455 456 InputStream inputStream = contentResolver.openInputStream(Uri.fromFile(file)); 457 458 assertThat(inputStream).isNotNull(); 459 assertThat(new String(inputStream.readAllBytes(), UTF_8)).isEqualTo("foo"); 460 } 461 462 @Test openInputStream_returnsProviderInputStream()463 public void openInputStream_returnsProviderInputStream() throws Exception { 464 ProviderInfo info = new ProviderInfo(); 465 info.authority = AUTHORITY; 466 ContentProvider myContentProvider = new MyContentProvider(); 467 myContentProvider.attachInfo(ApplicationProvider.getApplicationContext(), info); 468 ShadowContentResolver.registerProviderInternal(AUTHORITY, myContentProvider); 469 470 Uri uri = Uri.parse("content://" + AUTHORITY + "/some/path"); 471 InputStream actualInputStream = contentResolver.openInputStream(uri); 472 // Registered provider does not return named stream 473 assertThat(actualInputStream).isNotInstanceOf(NamedStream.class); 474 475 Uri otherUri = Uri.parse("content://otherAuthority/some/path"); 476 InputStream secondInputStream = contentResolver.openInputStream(otherUri); 477 // No registered provider results in named stream 478 assertThat(secondInputStream).isInstanceOf(NamedStream.class); 479 480 shadowContentResolver.registerInputStreamSupplier( 481 uri, () -> new ByteArrayInputStream("ourStream".getBytes(UTF_8))); 482 InputStream registeredInputStream = contentResolver.openInputStream(uri); 483 byte[] byteArray = new byte[registeredInputStream.available()]; 484 registeredInputStream.read(byteArray); 485 // Explicitly registered stream takes precedence 486 assertThat(byteArray).isEqualTo("ourStream".getBytes(UTF_8)); 487 } 488 489 @Test openOutputStream_withNoRealOrRegisteredProvider_doesNotThrow()490 public void openOutputStream_withNoRealOrRegisteredProvider_doesNotThrow() throws Exception { 491 Uri uri = Uri.parse("content://invalidauthority/test/1"); 492 assertThat(contentResolver.openOutputStream(uri)).isNotNull(); 493 } 494 495 @Test openOutputStream_withRealContentProvider_canReadBytesWrittenToOutputStream()496 public void openOutputStream_withRealContentProvider_canReadBytesWrittenToOutputStream() 497 throws IOException, RemoteException { 498 Robolectric.setupContentProvider(MyContentProvider.class, AUTHORITY); 499 Uri uri = Uri.parse("content://" + AUTHORITY + "/test/1"); 500 501 // Write content through given outputstream 502 try (OutputStream outputStream = contentResolver.openOutputStream(uri)) { 503 outputStream.write("foo".getBytes(UTF_8)); 504 } 505 506 // Verify written content can be read back 507 InputStream inputStream = contentResolver.openInputStream(uri); 508 assertThat(new String(inputStream.readAllBytes(), UTF_8)).isEqualTo("foo"); 509 } 510 511 @Test openOutputStream_shouldReturnRegisteredStream()512 public void openOutputStream_shouldReturnRegisteredStream() throws Exception { 513 final Uri uri = Uri.parse("content://registeredProvider/path"); 514 515 AtomicInteger callCount = new AtomicInteger(); 516 OutputStream outputStream = 517 new OutputStream() { 518 519 @Override 520 public void write(int arg0) throws IOException { 521 callCount.incrementAndGet(); 522 } 523 524 @Override 525 public String toString() { 526 return "outputstream for " + uri; 527 } 528 }; 529 530 shadowOf(contentResolver).registerOutputStream(uri, outputStream); 531 532 assertThat(callCount.get()).isEqualTo(0); 533 contentResolver.openOutputStream(uri).write(5); 534 assertThat(callCount.get()).isEqualTo(1); 535 536 contentResolver.openOutputStream(uri21).write(5); 537 assertThat(callCount.get()).isEqualTo(1); 538 } 539 540 @Test openOutputStream_shouldReturnNewStreamFromRegisteredSupplier()541 public void openOutputStream_shouldReturnNewStreamFromRegisteredSupplier() throws Exception { 542 final Uri uri = Uri.parse("content://registeredProvider/path"); 543 544 AtomicInteger streamCreateCount = new AtomicInteger(); 545 shadowOf(contentResolver) 546 .registerOutputStreamSupplier( 547 uri, 548 () -> { 549 streamCreateCount.incrementAndGet(); 550 AtomicBoolean isClosed = new AtomicBoolean(); 551 isClosed.set(false); 552 OutputStream outputStream = 553 new OutputStream() { 554 @Override 555 public void close() { 556 isClosed.set(true); 557 } 558 559 @Override 560 public void write(int arg0) throws IOException { 561 if (isClosed.get()) { 562 throw new IOException(); 563 } 564 } 565 566 @Override 567 public String toString() { 568 return "outputstream for " + uri; 569 } 570 }; 571 return outputStream; 572 }); 573 574 assertThat(streamCreateCount.get()).isEqualTo(0); 575 OutputStream outputStream1 = contentResolver.openOutputStream(uri); 576 outputStream1.close(); 577 assertThat(streamCreateCount.get()).isEqualTo(1); 578 579 contentResolver.openOutputStream(uri).write(5); 580 assertThat(streamCreateCount.get()).isEqualTo(2); 581 } 582 583 @Test openOutputStream_withModeWithNoRealOrRegisteredProvider_throws()584 public void openOutputStream_withModeWithNoRealOrRegisteredProvider_throws() { 585 Uri uri = Uri.parse("content://invalidauthority/test/1"); 586 assertThrows(FileNotFoundException.class, () -> contentResolver.openOutputStream(uri, "wt")); 587 } 588 589 @Test openOutputStream_withModeWithRealContentProvider_canReadBytesWrittenToOutputStream()590 public void openOutputStream_withModeWithRealContentProvider_canReadBytesWrittenToOutputStream() 591 throws IOException, RemoteException { 592 Robolectric.setupContentProvider(MyContentProvider.class, AUTHORITY); 593 Uri uri = Uri.parse("content://" + AUTHORITY + "/test/1"); 594 595 // Write content through given outputstream 596 try (OutputStream outputStream = contentResolver.openOutputStream(uri, "wt")) { 597 outputStream.write("foo".getBytes(UTF_8)); 598 } 599 600 // Verify written content can be read back 601 InputStream inputStream = contentResolver.openInputStream(uri); 602 assertThat(new String(inputStream.readAllBytes(), UTF_8)).isEqualTo("foo"); 603 } 604 605 @Test openOutputStream_withModeShouldReturnRegisteredStream()606 public void openOutputStream_withModeShouldReturnRegisteredStream() throws Exception { 607 final Uri uri = Uri.parse("content://registeredProvider/path"); 608 609 AtomicInteger callCount = new AtomicInteger(); 610 OutputStream outputStream = 611 new OutputStream() { 612 613 @Override 614 public void write(int arg0) throws IOException { 615 callCount.incrementAndGet(); 616 } 617 618 @Override 619 public String toString() { 620 return "outputstream for " + uri; 621 } 622 }; 623 624 shadowOf(contentResolver).registerOutputStream(uri, outputStream); 625 626 assertThat(callCount.get()).isEqualTo(0); 627 contentResolver.openOutputStream(uri, "wt").write(5); 628 assertThat(callCount.get()).isEqualTo(1); 629 } 630 631 @Test openOutputStream_withModeShouldReturnNewStreamFromRegisteredSupplier()632 public void openOutputStream_withModeShouldReturnNewStreamFromRegisteredSupplier() 633 throws Exception { 634 final Uri uri = Uri.parse("content://registeredProvider/path"); 635 636 AtomicInteger streamCreateCount = new AtomicInteger(); 637 shadowOf(contentResolver) 638 .registerOutputStreamSupplier( 639 uri, 640 () -> { 641 streamCreateCount.incrementAndGet(); 642 AtomicBoolean isClosed = new AtomicBoolean(); 643 isClosed.set(false); 644 OutputStream outputStream = 645 new OutputStream() { 646 @Override 647 public void close() { 648 isClosed.set(true); 649 } 650 651 @Override 652 public void write(int arg0) throws IOException { 653 if (isClosed.get()) { 654 throw new IOException(); 655 } 656 } 657 658 @Override 659 public String toString() { 660 return "outputstream for " + uri; 661 } 662 }; 663 return outputStream; 664 }); 665 666 assertThat(streamCreateCount.get()).isEqualTo(0); 667 OutputStream outputStream1 = contentResolver.openOutputStream(uri, "wt"); 668 outputStream1.close(); 669 assertThat(streamCreateCount.get()).isEqualTo(1); 670 671 contentResolver.openOutputStream(uri, "wt").write(5); 672 assertThat(streamCreateCount.get()).isEqualTo(2); 673 } 674 675 @Test shouldTrackNotifiedUris()676 public void shouldTrackNotifiedUris() { 677 contentResolver.notifyChange(Uri.parse("foo"), null, true); 678 contentResolver.notifyChange(Uri.parse("bar"), null); 679 680 assertThat(shadowContentResolver.getNotifiedUris().size()).isEqualTo(2); 681 ShadowContentResolver.NotifiedUri uri = shadowContentResolver.getNotifiedUris().get(0); 682 683 assertThat(uri.uri.toString()).isEqualTo("foo"); 684 assertThat(uri.syncToNetwork).isTrue(); 685 assertThat(uri.observer).isNull(); 686 687 uri = shadowContentResolver.getNotifiedUris().get(1); 688 689 assertThat(uri.uri.toString()).isEqualTo("bar"); 690 assertThat(uri.syncToNetwork).isFalse(); 691 assertThat(uri.observer).isNull(); 692 } 693 694 @Test 695 @Config(minSdk = N) notifyChangeWithFlags_shouldTrackNotifiedUris()696 public void notifyChangeWithFlags_shouldTrackNotifiedUris() { 697 contentResolver.notifyChange(Uri.parse("foo"), null, ContentResolver.NOTIFY_SYNC_TO_NETWORK); 698 contentResolver.notifyChange(Uri.parse("bar"), null, ContentResolver.NOTIFY_UPDATE); 699 700 assertThat(shadowContentResolver.getNotifiedUris().size()).isEqualTo(2); 701 702 ShadowContentResolver.NotifiedUri uri = shadowContentResolver.getNotifiedUris().get(0); 703 704 assertThat(uri.uri.toString()).isEqualTo("foo"); 705 assertThat(uri.syncToNetwork).isTrue(); 706 assertThat(uri.observer).isNull(); 707 assertThat(uri.flags).isEqualTo(ContentResolver.NOTIFY_SYNC_TO_NETWORK); 708 709 uri = shadowContentResolver.getNotifiedUris().get(1); 710 711 assertThat(uri.uri.toString()).isEqualTo("bar"); 712 assertThat(uri.syncToNetwork).isFalse(); 713 assertThat(uri.observer).isNull(); 714 assertThat(uri.flags).isEqualTo(ContentResolver.NOTIFY_UPDATE); 715 } 716 717 @SuppressWarnings("serial") 718 @Test applyBatchForRegisteredProvider()719 public void applyBatchForRegisteredProvider() 720 throws RemoteException, OperationApplicationException { 721 final List<String> operations = new ArrayList<>(); 722 ShadowContentResolver.registerProviderInternal( 723 "registeredProvider", 724 new ContentProvider() { 725 @Override 726 public boolean onCreate() { 727 return true; 728 } 729 730 @Override 731 public Cursor query( 732 Uri uri, 733 String[] projection, 734 String selection, 735 String[] selectionArgs, 736 String sortOrder) { 737 operations.add("query"); 738 MatrixCursor cursor = new MatrixCursor(new String[] {"a"}); 739 cursor.addRow(new Object[] {"b"}); 740 return cursor; 741 } 742 743 @Override 744 public String getType(Uri uri) { 745 return null; 746 } 747 748 @Override 749 public Uri insert(Uri uri, ContentValues values) { 750 operations.add("insert"); 751 return ContentUris.withAppendedId(uri, 1); 752 } 753 754 @Override 755 public int delete(Uri uri, String selection, String[] selectionArgs) { 756 operations.add("delete"); 757 return 0; 758 } 759 760 @Override 761 public int update( 762 Uri uri, ContentValues values, String selection, String[] selectionArgs) { 763 operations.add("update"); 764 return 0; 765 } 766 }); 767 768 final Uri uri = Uri.parse("content://registeredProvider/path"); 769 List<ContentProviderOperation> contentProviderOperations = 770 Arrays.asList( 771 ContentProviderOperation.newInsert(uri).withValue("a", "b").build(), 772 ContentProviderOperation.newUpdate(uri).withValue("a", "b").build(), 773 ContentProviderOperation.newDelete(uri).build(), 774 ContentProviderOperation.newAssertQuery(uri).withValue("a", "b").build()); 775 contentResolver.applyBatch("registeredProvider", new ArrayList<>(contentProviderOperations)); 776 777 assertThat(operations).containsExactly("insert", "update", "delete", "query"); 778 } 779 780 @Test applyBatchForUnregisteredProvider()781 public void applyBatchForUnregisteredProvider() 782 throws RemoteException, OperationApplicationException { 783 List<ContentProviderOperation> resultOperations = 784 shadowContentResolver.getContentProviderOperations(AUTHORITY); 785 assertThat(resultOperations).isNotNull(); 786 assertThat(resultOperations.size()).isEqualTo(0); 787 788 Uri uri = Uri.parse("content://org.robolectric"); 789 ArrayList<ContentProviderOperation> operations = new ArrayList<>(); 790 operations.add( 791 ContentProviderOperation.newInsert(uri) 792 .withValue("column1", "foo") 793 .withValue("column2", 5) 794 .build()); 795 operations.add( 796 ContentProviderOperation.newUpdate(uri) 797 .withSelection("id_column", new String[] {"99"}) 798 .withValue("column1", "bar") 799 .build()); 800 operations.add( 801 ContentProviderOperation.newDelete(uri) 802 .withSelection("id_column", new String[] {"11"}) 803 .build()); 804 ContentProviderResult[] result = contentResolver.applyBatch(AUTHORITY, operations); 805 806 resultOperations = shadowContentResolver.getContentProviderOperations(AUTHORITY); 807 assertThat(resultOperations).isEqualTo(operations); 808 assertThat(result).isNotNull(); 809 } 810 811 @Test shouldKeepTrackOfSyncRequests()812 public void shouldKeepTrackOfSyncRequests() { 813 ShadowContentResolver.Status status = ShadowContentResolver.getStatus(a, AUTHORITY, true); 814 assertThat(status).isNotNull(); 815 assertThat(status.syncRequests).isEqualTo(0); 816 ContentResolver.requestSync(a, AUTHORITY, new Bundle()); 817 assertThat(status.syncRequests).isEqualTo(1); 818 assertThat(status.syncExtras).isNotNull(); 819 } 820 821 @Test shouldKnowIfSyncIsActive()822 public void shouldKnowIfSyncIsActive() { 823 assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isFalse(); 824 ContentResolver.requestSync(a, AUTHORITY, new Bundle()); 825 assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isTrue(); 826 } 827 828 @Test shouldGetCurrentSyncs()829 public void shouldGetCurrentSyncs() { 830 ContentResolver.requestSync(a, AUTHORITY, new Bundle()); 831 ContentResolver.requestSync(b, AUTHORITY, new Bundle()); 832 833 List<SyncInfo> syncs = ContentResolver.getCurrentSyncs(); 834 assertThat(syncs.size()).isEqualTo(2); 835 836 SyncInfo syncA = Iterables.find(syncs, s -> s.account.equals(a)); 837 assertThat(syncA.account).isEqualTo(a); 838 assertThat(syncA.authority).isEqualTo(AUTHORITY); 839 840 SyncInfo syncB = Iterables.find(syncs, s -> s.account.equals(b)); 841 assertThat(syncB.account).isEqualTo(b); 842 assertThat(syncB.authority).isEqualTo(AUTHORITY); 843 844 ContentResolver.cancelSync(a, AUTHORITY); 845 List<SyncInfo> syncsAgain = ContentResolver.getCurrentSyncs(); 846 assertThat(syncsAgain.size()).isEqualTo(1); 847 848 SyncInfo firstAgain = syncsAgain.get(0); 849 assertThat(firstAgain.account).isEqualTo(b); 850 assertThat(firstAgain.authority).isEqualTo(AUTHORITY); 851 852 ContentResolver.cancelSync(b, AUTHORITY); 853 List<SyncInfo> s = ContentResolver.getCurrentSyncs(); 854 assertThat(s.size()).isEqualTo(0); 855 } 856 857 @Test shouldCancelSync()858 public void shouldCancelSync() { 859 ContentResolver.requestSync(a, AUTHORITY, new Bundle()); 860 ContentResolver.requestSync(b, AUTHORITY, new Bundle()); 861 assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isTrue(); 862 assertThat(ContentResolver.isSyncActive(b, AUTHORITY)).isTrue(); 863 864 ContentResolver.cancelSync(a, AUTHORITY); 865 assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isFalse(); 866 assertThat(ContentResolver.isSyncActive(b, AUTHORITY)).isTrue(); 867 } 868 869 @Test shouldSetIsSyncable()870 public void shouldSetIsSyncable() { 871 assertThat(ContentResolver.getIsSyncable(a, AUTHORITY)).isEqualTo(-1); 872 assertThat(ContentResolver.getIsSyncable(b, AUTHORITY)).isEqualTo(-1); 873 ContentResolver.setIsSyncable(a, AUTHORITY, 1); 874 ContentResolver.setIsSyncable(b, AUTHORITY, 2); 875 assertThat(ContentResolver.getIsSyncable(a, AUTHORITY)).isEqualTo(1); 876 assertThat(ContentResolver.getIsSyncable(b, AUTHORITY)).isEqualTo(2); 877 } 878 879 @Test shouldSetSyncAutomatically()880 public void shouldSetSyncAutomatically() { 881 assertThat(ContentResolver.getSyncAutomatically(a, AUTHORITY)).isFalse(); 882 ContentResolver.setSyncAutomatically(a, AUTHORITY, true); 883 assertThat(ContentResolver.getSyncAutomatically(a, AUTHORITY)).isTrue(); 884 } 885 886 @Test shouldAddPeriodicSync()887 public void shouldAddPeriodicSync() { 888 Bundle fooBar = new Bundle(); 889 fooBar.putString("foo", "bar"); 890 Bundle fooBaz = new Bundle(); 891 fooBaz.putString("foo", "baz"); 892 893 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 6000L); 894 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBaz, 6000L); 895 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBar, 6000L); 896 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 6000L); 897 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)) 898 .containsExactly( 899 new PeriodicSync(a, AUTHORITY, fooBar, 6000L), 900 new PeriodicSync(a, AUTHORITY, fooBaz, 6000L)); 901 assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)) 902 .containsExactly( 903 new PeriodicSync(b, AUTHORITY, fooBar, 6000L), 904 new PeriodicSync(b, AUTHORITY, fooBaz, 6000L)); 905 906 // If same extras, but different time, simply update the time. 907 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 42L); 908 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 42L); 909 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)) 910 .containsExactly( 911 new PeriodicSync(a, AUTHORITY, fooBar, 42L), 912 new PeriodicSync(a, AUTHORITY, fooBaz, 6000L)); 913 assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)) 914 .containsExactly( 915 new PeriodicSync(b, AUTHORITY, fooBar, 6000L), 916 new PeriodicSync(b, AUTHORITY, fooBaz, 42L)); 917 } 918 919 @Test shouldRemovePeriodSync()920 public void shouldRemovePeriodSync() { 921 Bundle fooBar = new Bundle(); 922 fooBar.putString("foo", "bar"); 923 Bundle fooBaz = new Bundle(); 924 fooBaz.putString("foo", "baz"); 925 Bundle foo42 = new Bundle(); 926 foo42.putInt("foo", 42); 927 assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).isEmpty(); 928 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).isEmpty(); 929 930 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 6000L); 931 ContentResolver.addPeriodicSync(a, AUTHORITY, fooBaz, 6000L); 932 ContentResolver.addPeriodicSync(a, AUTHORITY, foo42, 6000L); 933 934 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBar, 6000L); 935 ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 6000L); 936 ContentResolver.addPeriodicSync(b, AUTHORITY, foo42, 6000L); 937 938 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)) 939 .containsExactly( 940 new PeriodicSync(a, AUTHORITY, fooBar, 6000L), 941 new PeriodicSync(a, AUTHORITY, fooBaz, 6000L), 942 new PeriodicSync(a, AUTHORITY, foo42, 6000L)); 943 944 ContentResolver.removePeriodicSync(a, AUTHORITY, fooBar); 945 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)) 946 .containsExactly( 947 new PeriodicSync(a, AUTHORITY, fooBaz, 6000L), 948 new PeriodicSync(a, AUTHORITY, foo42, 6000L)); 949 950 ContentResolver.removePeriodicSync(a, AUTHORITY, fooBaz); 951 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)) 952 .containsExactly(new PeriodicSync(a, AUTHORITY, foo42, 6000L)); 953 954 ContentResolver.removePeriodicSync(a, AUTHORITY, foo42); 955 assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).isEmpty(); 956 assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)) 957 .containsExactly( 958 new PeriodicSync(b, AUTHORITY, fooBar, 6000L), 959 new PeriodicSync(b, AUTHORITY, fooBaz, 6000L), 960 new PeriodicSync(b, AUTHORITY, foo42, 6000L)); 961 } 962 963 @Test shouldGetPeriodSyncs()964 public void shouldGetPeriodSyncs() { 965 assertThat(ContentResolver.getPeriodicSyncs(a, AUTHORITY).size()).isEqualTo(0); 966 ContentResolver.addPeriodicSync(a, AUTHORITY, new Bundle(), 6000L); 967 968 List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(a, AUTHORITY); 969 assertThat(syncs.size()).isEqualTo(1); 970 971 PeriodicSync first = syncs.get(0); 972 assertThat(first.account).isEqualTo(a); 973 assertThat(first.authority).isEqualTo(AUTHORITY); 974 assertThat(first.period).isEqualTo(6000L); 975 assertThat(first.extras).isNotNull(); 976 } 977 978 @Test shouldValidateSyncExtras()979 public void shouldValidateSyncExtras() { 980 Bundle bundle = new Bundle(); 981 bundle.putString("foo", "strings"); 982 bundle.putLong("long", 10L); 983 bundle.putDouble("double", 10.0d); 984 bundle.putFloat("float", 10.0f); 985 bundle.putInt("int", 10); 986 bundle.putParcelable("account", a); 987 ContentResolver.validateSyncExtrasBundle(bundle); 988 } 989 990 @Test(expected = IllegalArgumentException.class) shouldValidateSyncExtrasAndThrow()991 public void shouldValidateSyncExtrasAndThrow() { 992 Bundle bundle = new Bundle(); 993 bundle.putParcelable("intent", new Intent()); 994 ContentResolver.validateSyncExtrasBundle(bundle); 995 } 996 997 @Test shouldSetMasterSyncAutomatically()998 public void shouldSetMasterSyncAutomatically() { 999 assertThat(ContentResolver.getMasterSyncAutomatically()).isFalse(); 1000 ContentResolver.setMasterSyncAutomatically(true); 1001 assertThat(ContentResolver.getMasterSyncAutomatically()).isTrue(); 1002 } 1003 1004 @Test shouldDelegateCallsToRegisteredProvider()1005 public void shouldDelegateCallsToRegisteredProvider() { 1006 ShadowContentResolver.registerProviderInternal( 1007 AUTHORITY, 1008 new ContentProvider() { 1009 @Override 1010 public boolean onCreate() { 1011 return false; 1012 } 1013 1014 @Override 1015 public Cursor query( 1016 Uri uri, 1017 String[] projection, 1018 String selection, 1019 String[] selectionArgs, 1020 String sortOrder) { 1021 return new BaseCursor(); 1022 } 1023 1024 @Override 1025 public Uri insert(Uri uri, ContentValues values) { 1026 return null; 1027 } 1028 1029 @Override 1030 public int delete(Uri uri, String selection, String[] selectionArgs) { 1031 return -1; 1032 } 1033 1034 @Override 1035 public int update( 1036 Uri uri, ContentValues values, String selection, String[] selectionArgs) { 1037 return -1; 1038 } 1039 1040 @Override 1041 public String getType(Uri uri) { 1042 return null; 1043 } 1044 }); 1045 final Uri uri = Uri.parse("content://" + AUTHORITY + "/some/path"); 1046 final Uri unrelated = Uri.parse("content://unrelated/some/path"); 1047 1048 assertThat(contentResolver.query(uri, null, null, null, null)).isNotNull(); 1049 assertThat(contentResolver.insert(uri, new ContentValues())).isNull(); 1050 1051 assertThat(contentResolver.delete(uri, null, null)).isEqualTo(-1); 1052 assertThat(contentResolver.update(uri, new ContentValues(), null, null)).isEqualTo(-1); 1053 1054 assertThat(contentResolver.query(unrelated, null, null, null, null)).isNull(); 1055 assertThat(contentResolver.insert(unrelated, new ContentValues())).isNotNull(); 1056 assertThat(contentResolver.delete(unrelated, null, null)).isEqualTo(1); 1057 assertThat(contentResolver.update(unrelated, new ContentValues(), null, null)).isEqualTo(1); 1058 } 1059 1060 @Test shouldThrowConfiguredExceptionWhenRegisteringContentObservers()1061 public void shouldThrowConfiguredExceptionWhenRegisteringContentObservers() { 1062 ShadowContentResolver scr = shadowOf(contentResolver); 1063 scr.setRegisterContentProviderException(FAKE_URI, new SecurityException()); 1064 try { 1065 contentResolver.registerContentObserver(FAKE_URI, true, new TestContentObserver(null)); 1066 fail(); 1067 } catch (SecurityException expected) { 1068 } 1069 } 1070 1071 @Test shouldClearConfiguredExceptionForRegisteringContentObservers()1072 public void shouldClearConfiguredExceptionForRegisteringContentObservers() { 1073 ShadowContentResolver scr = shadowOf(contentResolver); 1074 scr.setRegisterContentProviderException(FAKE_URI, new SecurityException()); 1075 scr.clearRegisterContentProviderException(FAKE_URI); 1076 // Should not throw the SecurityException. 1077 contentResolver.registerContentObserver(FAKE_URI, true, new TestContentObserver(null)); 1078 } 1079 1080 @Test shouldRegisterContentObservers()1081 public void shouldRegisterContentObservers() { 1082 TestContentObserver co = new TestContentObserver(null); 1083 ShadowContentResolver scr = shadowOf(contentResolver); 1084 1085 assertThat(scr.getContentObservers(FAKE_URI)).isEmpty(); 1086 1087 contentResolver.registerContentObserver(FAKE_URI, true, co); 1088 1089 assertThat(scr.getContentObservers(FAKE_URI)).containsExactly((ContentObserver) co); 1090 1091 assertThat(co.changed).isFalse(); 1092 contentResolver.notifyChange(FAKE_URI, null); 1093 assertThat(co.changed).isTrue(); 1094 1095 contentResolver.unregisterContentObserver(co); 1096 assertThat(scr.getContentObservers(FAKE_URI)).isEmpty(); 1097 } 1098 1099 @Test shouldUnregisterContentObservers()1100 public void shouldUnregisterContentObservers() { 1101 TestContentObserver co = new TestContentObserver(null); 1102 ShadowContentResolver scr = shadowOf(contentResolver); 1103 contentResolver.registerContentObserver(FAKE_URI, true, co); 1104 assertThat(scr.getContentObservers(FAKE_URI)).contains(co); 1105 1106 contentResolver.unregisterContentObserver(co); 1107 assertThat(scr.getContentObservers(FAKE_URI)).isEmpty(); 1108 1109 assertThat(co.changed).isFalse(); 1110 contentResolver.notifyChange(FAKE_URI, null); 1111 assertThat(co.changed).isFalse(); 1112 } 1113 1114 @Test shouldNotifyChildContentObservers()1115 public void shouldNotifyChildContentObservers() throws Exception { 1116 TestContentObserver co1 = new TestContentObserver(null); 1117 TestContentObserver co2 = new TestContentObserver(null); 1118 1119 Uri childUri = FAKE_URI.buildUpon().appendPath("path").build(); 1120 1121 contentResolver.registerContentObserver(FAKE_URI, true, co1); 1122 contentResolver.registerContentObserver(childUri, false, co2); 1123 1124 co1.changed = co2.changed = false; 1125 contentResolver.notifyChange(childUri, null); 1126 assertThat(co1.changed).isTrue(); 1127 assertThat(co2.changed).isTrue(); 1128 1129 co1.changed = co2.changed = false; 1130 contentResolver.notifyChange(FAKE_URI, null); 1131 assertThat(co1.changed).isTrue(); 1132 assertThat(co2.changed).isFalse(); 1133 1134 co1.changed = co2.changed = false; 1135 contentResolver.notifyChange(childUri.buildUpon().appendPath("extra").build(), null); 1136 assertThat(co1.changed).isTrue(); 1137 assertThat(co2.changed).isFalse(); 1138 } 1139 1140 @Test getProvider_shouldCreateProviderFromManifest()1141 public void getProvider_shouldCreateProviderFromManifest() throws Exception { 1142 Uri uri = Uri.parse("content://org.robolectric.authority1/shadows"); 1143 ContentProvider provider = ShadowContentResolver.getProvider(uri); 1144 assertThat(provider).isNotNull(); 1145 assertThat(provider.getReadPermission()).isEqualTo("READ_PERMISSION"); 1146 assertThat(provider.getWritePermission()).isEqualTo("WRITE_PERMISSION"); 1147 assertThat(provider.getPathPermissions()).asList().hasSize(1); 1148 1149 // unfortunately, there is no direct way of testing if authority is set or not 1150 // however, it's checked in ContentProvider.Transport method calls (validateIncomingUri), so 1151 // it's the closest we can test against 1152 if (RuntimeEnvironment.getApiLevel() <= 28) { 1153 provider.getIContentProvider().getType(uri); // should not throw 1154 } else { 1155 // just call validateIncomingUri directly 1156 provider.validateIncomingUri(uri); 1157 } 1158 } 1159 1160 @Test 1161 @Config(manifest = NONE) 1162 @SuppressWarnings("RobolectricSystemContext") // preexisting when check was enabled getProvider_shouldNotReturnAnyProviderWhenManifestIsNull()1163 public void getProvider_shouldNotReturnAnyProviderWhenManifestIsNull() { 1164 Application application = new Application(); 1165 shadowOf(application).callAttach(RuntimeEnvironment.systemContext); 1166 assertThat(ShadowContentResolver.getProvider(Uri.parse("content://"))).isNull(); 1167 } 1168 1169 @Test openAssetFileDescriptor_shouldOpenDescriptor()1170 public void openAssetFileDescriptor_shouldOpenDescriptor() throws IOException { 1171 Robolectric.setupContentProvider(MyContentProvider.class, AUTHORITY); 1172 1173 try (AssetFileDescriptor afd = 1174 contentResolver.openAssetFileDescriptor( 1175 Uri.parse("content://" + AUTHORITY + "/whatever"), "r")) { 1176 FileDescriptor descriptor = afd.getFileDescriptor(); 1177 assertThat(descriptor).isNotNull(); 1178 } 1179 } 1180 1181 @Test openTypedAssetFileDescriptor_shouldOpenDescriptor()1182 public void openTypedAssetFileDescriptor_shouldOpenDescriptor() 1183 throws IOException, RemoteException { 1184 Robolectric.setupContentProvider(MyContentProvider.class, AUTHORITY); 1185 1186 try (AssetFileDescriptor afd = 1187 contentResolver.openTypedAssetFileDescriptor( 1188 Uri.parse("content://" + AUTHORITY + "/whatever"), "*/*", null)) { 1189 1190 FileDescriptor descriptor = afd.getFileDescriptor(); 1191 assertThat(descriptor).isNotNull(); 1192 } 1193 } 1194 1195 @Test takeAndReleasePersistableUriPermissions()1196 public void takeAndReleasePersistableUriPermissions() { 1197 List<UriPermission> permissions = contentResolver.getPersistedUriPermissions(); 1198 assertThat(permissions).isEmpty(); 1199 1200 // Take the read permission for the uri. 1201 Uri uri = Uri.parse("content://" + AUTHORITY + "/whatever"); 1202 contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 1203 assertThat(permissions).hasSize(1); 1204 assertThat(permissions.get(0).getUri()).isSameInstanceAs(uri); 1205 assertThat(permissions.get(0).isReadPermission()).isTrue(); 1206 assertThat(permissions.get(0).isWritePermission()).isFalse(); 1207 1208 // Take the write permission for the uri. 1209 contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 1210 assertThat(permissions).hasSize(1); 1211 assertThat(permissions.get(0).getUri()).isSameInstanceAs(uri); 1212 assertThat(permissions.get(0).isReadPermission()).isTrue(); 1213 assertThat(permissions.get(0).isWritePermission()).isTrue(); 1214 1215 // Release the read permission for the uri. 1216 contentResolver.releasePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 1217 assertThat(permissions).hasSize(1); 1218 assertThat(permissions.get(0).getUri()).isSameInstanceAs(uri); 1219 assertThat(permissions.get(0).isReadPermission()).isFalse(); 1220 assertThat(permissions.get(0).isWritePermission()).isTrue(); 1221 1222 // Release the write permission for the uri. 1223 contentResolver.releasePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 1224 assertThat(permissions).isEmpty(); 1225 } 1226 1227 @Test getSyncAdapterTypes()1228 public void getSyncAdapterTypes() { 1229 SyncAdapterType[] syncAdapterTypes = 1230 new SyncAdapterType[] { 1231 new SyncAdapterType( 1232 "authority1", 1233 "accountType1", 1234 /* userVisible= */ false, 1235 /* supportsUploading= */ false), 1236 new SyncAdapterType( 1237 "authority2", 1238 "accountType2", 1239 /* userVisible= */ true, 1240 /* supportsUploading= */ false), 1241 new SyncAdapterType( 1242 "authority3", "accountType3", /* userVisible= */ true, /* supportsUploading= */ true) 1243 }; 1244 1245 ShadowContentResolver.setSyncAdapterTypes(syncAdapterTypes); 1246 assertThat(ContentResolver.getSyncAdapterTypes()).isEqualTo(syncAdapterTypes); 1247 } 1248 1249 private static class QueryParamTrackingCursor extends BaseCursor { 1250 public Uri uri; 1251 public String[] projection; 1252 public String selection; 1253 public String[] selectionArgs; 1254 public String sortOrder; 1255 1256 @Override setQuery( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)1257 public void setQuery( 1258 Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 1259 this.uri = uri; 1260 this.projection = projection; 1261 this.selection = selection; 1262 this.selectionArgs = selectionArgs; 1263 this.sortOrder = sortOrder; 1264 } 1265 } 1266 1267 private static class TestContentObserver extends ContentObserver { TestContentObserver(Handler handler)1268 public TestContentObserver(Handler handler) { 1269 super(handler); 1270 } 1271 1272 public boolean changed = false; 1273 1274 @Override onChange(boolean selfChange)1275 public void onChange(boolean selfChange) { 1276 changed = true; 1277 } 1278 1279 @Override onChange(boolean selfChange, Uri uri)1280 public void onChange(boolean selfChange, Uri uri) { 1281 changed = true; 1282 } 1283 } 1284 1285 /** Provider that opens a temporary file. */ 1286 public static class MyContentProvider extends ContentProvider { 1287 @Override onCreate()1288 public boolean onCreate() { 1289 return true; 1290 } 1291 1292 @Override query(Uri uri, String[] strings, String s, String[] strings1, String s1)1293 public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) { 1294 return null; 1295 } 1296 1297 @Override getType(Uri uri)1298 public String getType(Uri uri) { 1299 return null; 1300 } 1301 1302 @Override insert(Uri uri, ContentValues contentValues)1303 public Uri insert(Uri uri, ContentValues contentValues) { 1304 return null; 1305 } 1306 1307 @Override delete(Uri uri, String s, String[] strings)1308 public int delete(Uri uri, String s, String[] strings) { 1309 return 0; 1310 } 1311 1312 @Override update(Uri uri, ContentValues contentValues, String s, String[] strings)1313 public int update(Uri uri, ContentValues contentValues, String s, String[] strings) { 1314 return 0; 1315 } 1316 1317 @Override openFile(Uri uri, String mode)1318 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 1319 final File file = 1320 new File(ApplicationProvider.getApplicationContext().getFilesDir(), "test_file"); 1321 try { 1322 file.createNewFile(); 1323 } catch (IOException e) { 1324 throw new RuntimeException("error creating new file", e); 1325 } 1326 return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE); 1327 } 1328 } 1329 } 1330