Home | History | Annotate | Download | only in shadows
      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
     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
     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
     90   public void getType_shouldDefaultToNull() {
     91     assertThat(contentResolver.getType(uri21)).isNull();
     92   }
     93 
     94   @Test
     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
    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
    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
    158   public void insert_supportsNullContentValues() {
    159     contentResolver.insert(EXTERNAL_CONTENT_URI, null);
    160     assertThat(shadowContentResolver.getInsertStatements().get(0).getContentValues()).isNull();
    161   }
    162 
    163   @Test
    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
    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
    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
    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
    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
    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
    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
    250   public void acquireUnstableProvider_shouldDefaultToNull() {
    251     assertThat(contentResolver.acquireUnstableProvider(uri21)).isNull();
    252   }
    253 
    254   @Test
    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
    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
    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
    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)
    305   public void openInputStream_shouldReturnAnInputStreamThatExceptionsOnRead() throws Exception {
    306     InputStream inputStream = contentResolver.openInputStream(uri21);
    307     inputStream.read();
    308   }
    309 
    310   @Test
    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
    320   public void openOutputStream_shouldReturnAnOutputStream() throws Exception {
    321     assertThat(contentResolver.openOutputStream(uri21)).isInstanceOf(OutputStream.class);
    322   }
    323 
    324   @Test
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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)
    604   public void shouldValidateSyncExtrasAndThrow() {
    605     Bundle bundle = new Bundle();
    606     bundle.putParcelable("intent", new Intent());
    607     ContentResolver.validateSyncExtrasBundle(bundle);
    608   }
    609 
    610   @Test
    611   public void shouldSetMasterSyncAutomatically() {
    612     assertThat(ContentResolver.getMasterSyncAutomatically()).isFalse();
    613     ContentResolver.setMasterSyncAutomatically(true);
    614     assertThat(ContentResolver.getMasterSyncAutomatically()).isTrue();
    615   }
    616 
    617   @Test
    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
    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
    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
    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
    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)
    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
    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)
    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
    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
    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 {
    827     public TestContentObserver(Handler handler) {
    828       super(handler);
    829     }
    830 
    831     public boolean changed = false;
    832 
    833     @Override
    834     public void onChange(boolean selfChange) {
    835       changed = true;
    836     }
    837 
    838     @Override
    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
    849     public boolean onCreate() {
    850       return true;
    851     }
    852 
    853     @Override
    854     public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
    855       return null;
    856     }
    857 
    858     @Override
    859     public String getType(Uri uri) {
    860       return null;
    861     }
    862 
    863     @Override
    864     public Uri insert(Uri uri, ContentValues contentValues) {
    865       return null;
    866     }
    867 
    868     @Override
    869     public int delete(Uri uri, String s, String[] strings) {
    870       return 0;
    871     }
    872 
    873     @Override
    874     public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
    875       return 0;
    876     }
    877 
    878     @Override
    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