• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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