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