blob: d78887a8d1bc03ab56345a6b23f8529178cca82d [file] [log] [blame]
package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.KITKAT;
import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.annotation.Config.NONE;
import android.accounts.Account;
import android.app.Application;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.PeriodicSync;
import android.content.SyncAdapterType;
import android.content.UriPermission;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.fakes.BaseCursor;
@RunWith(AndroidJUnit4.class)
public class ShadowContentResolverTest {
private static final String AUTHORITY = "org.robolectric";
private ContentResolver contentResolver;
private ShadowContentResolver shadowContentResolver;
private Uri uri21;
private Uri uri22;
private Account a, b;
@Before
public void setUp() {
contentResolver = ApplicationProvider.getApplicationContext().getContentResolver();
shadowContentResolver = shadowOf(contentResolver);
uri21 = Uri.parse(EXTERNAL_CONTENT_URI.toString() + "/21");
uri22 = Uri.parse(EXTERNAL_CONTENT_URI.toString() + "/22");
a = new Account("a", "type");
b = new Account("b", "type");
}
@Test
public void insert_shouldReturnIncreasingUris() {
shadowContentResolver.setNextDatabaseIdForInserts(20);
assertThat(contentResolver.insert(EXTERNAL_CONTENT_URI, new ContentValues())).isEqualTo(uri21);
assertThat(contentResolver.insert(EXTERNAL_CONTENT_URI, new ContentValues())).isEqualTo(uri22);
}
@Test
public void getType_shouldDefaultToNull() {
assertThat(contentResolver.getType(uri21)).isNull();
}
@Test
public void getType_shouldReturnProviderValue() {
ShadowContentResolver.registerProviderInternal(AUTHORITY, new ContentProvider() {
@Override public boolean onCreate() {
return false;
}
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return new BaseCursor();
}
@Override public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override public int delete(Uri uri, String selection, String[] selectionArgs) {
return -1;
}
@Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return -1;
}
@Override public String getType(Uri uri) {
return "mytype";
}
});
final Uri uri = Uri.parse("content://"+AUTHORITY+"/some/path");
assertThat(contentResolver.getType(uri)).isEqualTo("mytype");
}
@Test
public void insert_shouldTrackInsertStatements() {
ContentValues contentValues = new ContentValues();
contentValues.put("foo", "bar");
contentResolver.insert(EXTERNAL_CONTENT_URI, contentValues);
assertThat(shadowContentResolver.getInsertStatements().size()).isEqualTo(1);
assertThat(shadowContentResolver.getInsertStatements().get(0).getUri()).isEqualTo(EXTERNAL_CONTENT_URI);
assertThat(
shadowContentResolver
.getInsertStatements()
.get(0)
.getContentValues()
.getAsString("foo"))
.isEqualTo("bar");
contentValues = new ContentValues();
contentValues.put("hello", "world");
contentResolver.insert(EXTERNAL_CONTENT_URI, contentValues);
assertThat(shadowContentResolver.getInsertStatements().size()).isEqualTo(2);
assertThat(
shadowContentResolver
.getInsertStatements()
.get(1)
.getContentValues()
.getAsString("hello"))
.isEqualTo("world");
}
@Test
public void insert_shouldTrackUpdateStatements() {
ContentValues contentValues = new ContentValues();
contentValues.put("foo", "bar");
contentResolver.update(
EXTERNAL_CONTENT_URI, contentValues, "robolectric", new String[] {"awesome"});
assertThat(shadowContentResolver.getUpdateStatements().size()).isEqualTo(1);
assertThat(shadowContentResolver.getUpdateStatements().get(0).getUri()).isEqualTo(EXTERNAL_CONTENT_URI);
assertThat(
shadowContentResolver
.getUpdateStatements()
.get(0)
.getContentValues()
.getAsString("foo"))
.isEqualTo("bar");
assertThat(shadowContentResolver.getUpdateStatements().get(0).getWhere())
.isEqualTo("robolectric");
assertThat(shadowContentResolver.getUpdateStatements().get(0).getSelectionArgs())
.isEqualTo(new String[] {"awesome"});
contentValues = new ContentValues();
contentValues.put("hello", "world");
contentResolver.update(EXTERNAL_CONTENT_URI, contentValues, null, null);
assertThat(shadowContentResolver.getUpdateStatements().size()).isEqualTo(2);
assertThat(shadowContentResolver.getUpdateStatements().get(1).getUri()).isEqualTo(EXTERNAL_CONTENT_URI);
assertThat(
shadowContentResolver
.getUpdateStatements()
.get(1)
.getContentValues()
.getAsString("hello"))
.isEqualTo("world");
assertThat(shadowContentResolver.getUpdateStatements().get(1).getWhere()).isNull();
assertThat(shadowContentResolver.getUpdateStatements().get(1).getSelectionArgs()).isNull();
}
@Test
public void insert_supportsNullContentValues() {
contentResolver.insert(EXTERNAL_CONTENT_URI, null);
assertThat(shadowContentResolver.getInsertStatements().get(0).getContentValues()).isNull();
}
@Test
public void update_supportsNullContentValues() {
contentResolver.update(EXTERNAL_CONTENT_URI, null, null, null);
assertThat(shadowContentResolver.getUpdateStatements().get(0).getContentValues()).isNull();
}
@Test
public void delete_shouldTrackDeletedUris() {
assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(0);
assertThat(contentResolver.delete(uri21, null, null)).isEqualTo(1);
assertThat(shadowContentResolver.getDeletedUris()).contains(uri21);
assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(1);
assertThat(contentResolver.delete(uri22, null, null)).isEqualTo(1);
assertThat(shadowContentResolver.getDeletedUris()).contains(uri22);
assertThat(shadowContentResolver.getDeletedUris().size()).isEqualTo(2);
}
@Test
public void delete_shouldTrackDeletedStatements() {
assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(0);
assertThat(contentResolver.delete(uri21, "id", new String[]{"5"})).isEqualTo(1);
assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(1);
assertThat(shadowContentResolver.getDeleteStatements().get(0).getUri()).isEqualTo(uri21);
assertThat(shadowContentResolver.getDeleteStatements().get(0).getContentProvider()).isNull();
assertThat(shadowContentResolver.getDeleteStatements().get(0).getWhere()).isEqualTo("id");
assertThat(shadowContentResolver.getDeleteStatements().get(0).getSelectionArgs()[0])
.isEqualTo("5");
assertThat(contentResolver.delete(uri21, "foo", new String[]{"bar"})).isEqualTo(1);
assertThat(shadowContentResolver.getDeleteStatements().size()).isEqualTo(2);
assertThat(shadowContentResolver.getDeleteStatements().get(1).getUri()).isEqualTo(uri21);
assertThat(shadowContentResolver.getDeleteStatements().get(1).getWhere()).isEqualTo("foo");
assertThat(shadowContentResolver.getDeleteStatements().get(1).getSelectionArgs()[0])
.isEqualTo("bar");
}
@Test
public void whenCursorHasBeenSet_query_shouldReturnTheCursor() {
assertThat(shadowContentResolver.query(null, null, null, null, null)).isNull();
BaseCursor cursor = new BaseCursor();
shadowContentResolver.setCursor(cursor);
assertThat((BaseCursor) shadowContentResolver.query(null, null, null, null, null))
.isSameInstanceAs(cursor);
}
@Test
public void whenCursorHasBeenSet_queryWithCancellationSignal_shouldReturnTheCursor() {
assertThat(shadowContentResolver.query(null, null, null, null, null, new CancellationSignal())).isNull();
BaseCursor cursor = new BaseCursor();
shadowContentResolver.setCursor(cursor);
assertThat(
(BaseCursor)
shadowContentResolver.query(null, null, null, null, null, new CancellationSignal()))
.isSameInstanceAs(cursor);
}
@Test
public void query_shouldReturnSpecificCursorsForSpecificUris() {
assertThat(shadowContentResolver.query(uri21, null, null, null, null)).isNull();
assertThat(shadowContentResolver.query(uri22, null, null, null, null)).isNull();
BaseCursor cursor21 = new BaseCursor();
BaseCursor cursor22 = new BaseCursor();
shadowContentResolver.setCursor(uri21, cursor21);
shadowContentResolver.setCursor(uri22, cursor22);
assertThat((BaseCursor) shadowContentResolver.query(uri21, null, null, null, null))
.isSameInstanceAs(cursor21);
assertThat((BaseCursor) shadowContentResolver.query(uri22, null, null, null, null))
.isSameInstanceAs(cursor22);
}
@Test
public void query_shouldKnowWhatItsParamsWere() {
String[] projection = {};
String selection = "select";
String[] selectionArgs = {};
String sortOrder = "order";
QueryParamTrackingCursor testCursor = new QueryParamTrackingCursor();
shadowContentResolver.setCursor(testCursor);
Cursor cursor = shadowContentResolver.query(uri21, projection, selection, selectionArgs, sortOrder);
assertThat((QueryParamTrackingCursor) cursor).isEqualTo(testCursor);
assertThat(testCursor.uri).isEqualTo(uri21);
assertThat(testCursor.projection).isEqualTo(projection);
assertThat(testCursor.selection).isEqualTo(selection);
assertThat(testCursor.selectionArgs).isEqualTo(selectionArgs);
assertThat(testCursor.sortOrder).isEqualTo(sortOrder);
}
@Test
public void acquireUnstableProvider_shouldDefaultToNull() {
assertThat(contentResolver.acquireUnstableProvider(uri21)).isNull();
}
@Test
public void acquireUnstableProvider_shouldReturnWithUri() {
ContentProvider cp = mock(ContentProvider.class);
ShadowContentResolver.registerProviderInternal(AUTHORITY, cp);
final Uri uri = Uri.parse("content://" + AUTHORITY);
assertThat(contentResolver.acquireUnstableProvider(uri))
.isSameInstanceAs(cp.getIContentProvider());
}
@Test
public void acquireUnstableProvider_shouldReturnWithString() {
ContentProvider cp = mock(ContentProvider.class);
ShadowContentResolver.registerProviderInternal(AUTHORITY, cp);
assertThat(contentResolver.acquireUnstableProvider(AUTHORITY))
.isSameInstanceAs(cp.getIContentProvider());
}
@Test
public void call_shouldCallProvider() {
final String METHOD = "method";
final String ARG = "arg";
final Bundle EXTRAS = new Bundle();
final Uri uri = Uri.parse("content://" + AUTHORITY);
ContentProvider provider = mock(ContentProvider.class);
doReturn(null).when(provider).call(METHOD, ARG, EXTRAS);
ShadowContentResolver.registerProviderInternal(AUTHORITY, provider);
contentResolver.call(uri, METHOD, ARG, EXTRAS);
verify(provider).call(METHOD, ARG, EXTRAS);
}
@Test
public void registerProvider_shouldAttachProviderInfo() {
ContentProvider mock = mock(ContentProvider.class);
ProviderInfo providerInfo0 = new ProviderInfo();
providerInfo0.authority = "the-authority"; // todo: support multiple authorities
providerInfo0.grantUriPermissions = true;
mock.attachInfo(ApplicationProvider.getApplicationContext(), providerInfo0);
mock.onCreate();
ArgumentCaptor<ProviderInfo> captor = ArgumentCaptor.forClass(ProviderInfo.class);
verify(mock)
.attachInfo(
same((Application) ApplicationProvider.getApplicationContext()), captor.capture());
ProviderInfo providerInfo = captor.getValue();
assertThat(providerInfo.authority).isEqualTo("the-authority");
assertThat(providerInfo.grantUriPermissions).isEqualTo(true);
}
@Test(expected = UnsupportedOperationException.class)
public void openInputStream_shouldReturnAnInputStreamThatExceptionsOnRead() throws Exception {
InputStream inputStream = contentResolver.openInputStream(uri21);
inputStream.read();
}
@Test
public void openInputStream_returnsPreRegisteredStream() throws Exception {
shadowContentResolver.registerInputStream(uri21, new ByteArrayInputStream("ourStream".getBytes(UTF_8)));
InputStream inputStream = contentResolver.openInputStream(uri21);
byte[] data = new byte[9];
inputStream.read(data);
assertThat(new String(data, UTF_8)).isEqualTo("ourStream");
}
@Test
public void openInputStream_returnsNewStreamEachTimeFromRegisteredSupplier() throws Exception {
shadowContentResolver.registerInputStreamSupplier(
uri21, () -> new ByteArrayInputStream("ourStream".getBytes(UTF_8)));
InputStream inputStream1 = contentResolver.openInputStream(uri21);
byte[] data1 = new byte[9];
inputStream1.read(data1);
inputStream1.close();
InputStream inputStream2 = contentResolver.openInputStream(uri21);
byte[] data2 = new byte[9];
inputStream2.read(data2);
inputStream2.close();
assertThat(new String(data1, UTF_8)).isEqualTo("ourStream");
assertThat(new String(data2, UTF_8)).isEqualTo("ourStream");
}
@Test
public void openOutputStream_shouldReturnAnOutputStream() throws Exception {
assertThat(contentResolver.openOutputStream(uri21)).isInstanceOf(OutputStream.class);
}
@Test
public void openOutputStream_shouldReturnRegisteredStream() throws Exception {
final Uri uri = Uri.parse("content://registeredProvider/path");
AtomicInteger callCount = new AtomicInteger();
OutputStream outputStream =
new OutputStream() {
@Override
public void write(int arg0) throws IOException {
callCount.incrementAndGet();
}
@Override
public String toString() {
return "outputstream for " + uri;
}
};
shadowOf(contentResolver).registerOutputStream(uri, outputStream);
assertThat(callCount.get()).isEqualTo(0);
contentResolver.openOutputStream(uri).write(5);
assertThat(callCount.get()).isEqualTo(1);
contentResolver.openOutputStream(uri21).write(5);
assertThat(callCount.get()).isEqualTo(1);
}
@Test
public void openOutputStream_shouldReturnNewStreamFromRegisteredSupplier() throws Exception {
final Uri uri = Uri.parse("content://registeredProvider/path");
AtomicInteger streamCreateCount = new AtomicInteger();
shadowOf(contentResolver)
.registerOutputStreamSupplier(
uri,
() -> {
streamCreateCount.incrementAndGet();
AtomicBoolean isClosed = new AtomicBoolean();
isClosed.set(false);
OutputStream outputStream =
new OutputStream() {
@Override
public void close() {
isClosed.set(true);
}
@Override
public void write(int arg0) throws IOException {
if (isClosed.get()) {
throw new IOException();
}
}
@Override
public String toString() {
return "outputstream for " + uri;
}
};
return outputStream;
});
assertThat(streamCreateCount.get()).isEqualTo(0);
OutputStream outputStream1 = contentResolver.openOutputStream(uri);
outputStream1.close();
assertThat(streamCreateCount.get()).isEqualTo(1);
contentResolver.openOutputStream(uri).write(5);
assertThat(streamCreateCount.get()).isEqualTo(2);
}
@Test
public void shouldTrackNotifiedUris() {
contentResolver.notifyChange(Uri.parse("foo"), null, true);
contentResolver.notifyChange(Uri.parse("bar"), null);
assertThat(shadowContentResolver.getNotifiedUris().size()).isEqualTo(2);
ShadowContentResolver.NotifiedUri uri = shadowContentResolver.getNotifiedUris().get(0);
assertThat(uri.uri.toString()).isEqualTo("foo");
assertThat(uri.syncToNetwork).isTrue();
assertThat(uri.observer).isNull();
uri = shadowContentResolver.getNotifiedUris().get(1);
assertThat(uri.uri.toString()).isEqualTo("bar");
assertThat(uri.syncToNetwork).isFalse();
assertThat(uri.observer).isNull();
}
@SuppressWarnings("serial")
@Test
public void applyBatchForRegisteredProvider() throws RemoteException, OperationApplicationException {
final List<String> operations = new ArrayList<>();
ShadowContentResolver.registerProviderInternal("registeredProvider", new ContentProvider() {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
operations.add("query");
MatrixCursor cursor = new MatrixCursor(new String[] {"a"});
cursor.addRow(new Object[] {"b"});
return cursor;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
operations.add("insert");
return ContentUris.withAppendedId(uri, 1);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
operations.add("delete");
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
operations.add("update");
return 0;
}
});
final Uri uri = Uri.parse("content://registeredProvider/path");
List<ContentProviderOperation> contentProviderOperations =
Arrays.asList(
ContentProviderOperation.newInsert(uri).withValue("a", "b").build(),
ContentProviderOperation.newUpdate(uri).withValue("a", "b").build(),
ContentProviderOperation.newDelete(uri).build(),
ContentProviderOperation.newAssertQuery(uri).withValue("a", "b").build());
contentResolver.applyBatch("registeredProvider", new ArrayList<>(contentProviderOperations));
assertThat(operations).containsExactly("insert", "update", "delete", "query");
}
@Test
public void applyBatchForUnregisteredProvider() throws RemoteException, OperationApplicationException {
List<ContentProviderOperation> resultOperations = shadowContentResolver.getContentProviderOperations(AUTHORITY);
assertThat(resultOperations).isNotNull();
assertThat(resultOperations.size()).isEqualTo(0);
ContentProviderResult[] contentProviderResults = new ContentProviderResult[] {
new ContentProviderResult(1),
new ContentProviderResult(1),
};
shadowContentResolver.setContentProviderResult(contentProviderResults);
Uri uri = Uri.parse("content://org.robolectric");
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
operations.add(ContentProviderOperation.newInsert(uri)
.withValue("column1", "foo")
.withValue("column2", 5)
.build());
operations.add(ContentProviderOperation.newUpdate(uri)
.withSelection("id_column", new String[] { "99" })
.withValue("column1", "bar")
.build());
operations.add(ContentProviderOperation.newDelete(uri)
.withSelection("id_column", new String[] { "11" })
.build());
ContentProviderResult[] result = contentResolver.applyBatch(AUTHORITY, operations);
resultOperations = shadowContentResolver.getContentProviderOperations(AUTHORITY);
assertThat(resultOperations).isEqualTo(operations);
assertThat(result).isEqualTo(contentProviderResults);
}
@Test
public void shouldKeepTrackOfSyncRequests() {
ShadowContentResolver.Status status = ShadowContentResolver.getStatus(a, AUTHORITY, true);
assertThat(status).isNotNull();
assertThat(status.syncRequests).isEqualTo(0);
ContentResolver.requestSync(a, AUTHORITY, new Bundle());
assertThat(status.syncRequests).isEqualTo(1);
assertThat(status.syncExtras).isNotNull();
}
@Test
public void shouldKnowIfSyncIsActive() {
assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isFalse();
ContentResolver.requestSync(a, AUTHORITY, new Bundle());
assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isTrue();
}
@Test
public void shouldCancelSync() {
ContentResolver.requestSync(a, AUTHORITY, new Bundle());
ContentResolver.requestSync(b, AUTHORITY, new Bundle());
assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isTrue();
assertThat(ContentResolver.isSyncActive(b, AUTHORITY)).isTrue();
ContentResolver.cancelSync(a, AUTHORITY);
assertThat(ContentResolver.isSyncActive(a, AUTHORITY)).isFalse();
assertThat(ContentResolver.isSyncActive(b, AUTHORITY)).isTrue();
}
@Test
public void shouldSetIsSyncable() {
assertThat(ContentResolver.getIsSyncable(a, AUTHORITY)).isEqualTo(-1);
assertThat(ContentResolver.getIsSyncable(b, AUTHORITY)).isEqualTo(-1);
ContentResolver.setIsSyncable(a, AUTHORITY, 1);
ContentResolver.setIsSyncable(b, AUTHORITY, 2);
assertThat(ContentResolver.getIsSyncable(a, AUTHORITY)).isEqualTo(1);
assertThat(ContentResolver.getIsSyncable(b, AUTHORITY)).isEqualTo(2);
}
@Test
public void shouldSetSyncAutomatically() {
assertThat(ContentResolver.getSyncAutomatically(a, AUTHORITY)).isFalse();
ContentResolver.setSyncAutomatically(a, AUTHORITY, true);
assertThat(ContentResolver.getSyncAutomatically(a, AUTHORITY)).isTrue();
}
@Test
public void shouldAddPeriodicSync() {
Bundle fooBar = new Bundle();
fooBar.putString("foo", "bar");
Bundle fooBaz = new Bundle();
fooBaz.putString("foo", "baz");
ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 6000L);
ContentResolver.addPeriodicSync(a, AUTHORITY, fooBaz, 6000L);
ContentResolver.addPeriodicSync(b, AUTHORITY, fooBar, 6000L);
ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 6000L);
assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsExactly(
new PeriodicSync(a, AUTHORITY, fooBar, 6000L),
new PeriodicSync(a, AUTHORITY, fooBaz, 6000L));
assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).containsExactly(
new PeriodicSync(b, AUTHORITY, fooBar, 6000L),
new PeriodicSync(b, AUTHORITY, fooBaz, 6000L));
// If same extras, but different time, simply update the time.
ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 42L);
ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 42L);
assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsExactly(
new PeriodicSync(a, AUTHORITY, fooBar, 42L),
new PeriodicSync(a, AUTHORITY, fooBaz, 6000L));
assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).containsExactly(
new PeriodicSync(b, AUTHORITY, fooBar, 6000L),
new PeriodicSync(b, AUTHORITY, fooBaz, 42L));
}
@Test
public void shouldRemovePeriodSync() {
Bundle fooBar = new Bundle();
fooBar.putString("foo", "bar");
Bundle fooBaz = new Bundle();
fooBaz.putString("foo", "baz");
Bundle foo42 = new Bundle();
foo42.putInt("foo", 42);
assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).isEmpty();
assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).isEmpty();
ContentResolver.addPeriodicSync(a, AUTHORITY, fooBar, 6000L);
ContentResolver.addPeriodicSync(a, AUTHORITY, fooBaz, 6000L);
ContentResolver.addPeriodicSync(a, AUTHORITY, foo42, 6000L);
ContentResolver.addPeriodicSync(b, AUTHORITY, fooBar, 6000L);
ContentResolver.addPeriodicSync(b, AUTHORITY, fooBaz, 6000L);
ContentResolver.addPeriodicSync(b, AUTHORITY, foo42, 6000L);
assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsExactly(
new PeriodicSync(a, AUTHORITY, fooBar, 6000L),
new PeriodicSync(a, AUTHORITY, fooBaz, 6000L),
new PeriodicSync(a, AUTHORITY, foo42, 6000L));
ContentResolver.removePeriodicSync(a, AUTHORITY, fooBar);
assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsExactly(
new PeriodicSync(a, AUTHORITY, fooBaz, 6000L),
new PeriodicSync(a, AUTHORITY, foo42, 6000L));
ContentResolver.removePeriodicSync(a, AUTHORITY, fooBaz);
assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).containsExactly(
new PeriodicSync(a, AUTHORITY, foo42, 6000L));
ContentResolver.removePeriodicSync(a, AUTHORITY, foo42);
assertThat(ShadowContentResolver.getPeriodicSyncs(a, AUTHORITY)).isEmpty();
assertThat(ShadowContentResolver.getPeriodicSyncs(b, AUTHORITY)).containsExactly(
new PeriodicSync(b, AUTHORITY, fooBar, 6000L),
new PeriodicSync(b, AUTHORITY, fooBaz, 6000L),
new PeriodicSync(b, AUTHORITY, foo42, 6000L));
}
@Test
public void shouldGetPeriodSyncs() {
assertThat(ContentResolver.getPeriodicSyncs(a, AUTHORITY).size()).isEqualTo(0);
ContentResolver.addPeriodicSync(a, AUTHORITY, new Bundle(), 6000L);
List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(a, AUTHORITY);
assertThat(syncs.size()).isEqualTo(1);
PeriodicSync first = syncs.get(0);
assertThat(first.account).isEqualTo(a);
assertThat(first.authority).isEqualTo(AUTHORITY);
assertThat(first.period).isEqualTo(6000L);
assertThat(first.extras).isNotNull();
}
@Test
public void shouldValidateSyncExtras() {
Bundle bundle = new Bundle();
bundle.putString("foo", "strings");
bundle.putLong("long", 10L);
bundle.putDouble("double", 10.0d);
bundle.putFloat("float", 10.0f);
bundle.putInt("int", 10);
bundle.putParcelable("account", a);
ContentResolver.validateSyncExtrasBundle(bundle);
}
@Test(expected = IllegalArgumentException.class)
public void shouldValidateSyncExtrasAndThrow() {
Bundle bundle = new Bundle();
bundle.putParcelable("intent", new Intent());
ContentResolver.validateSyncExtrasBundle(bundle);
}
@Test
public void shouldSetMasterSyncAutomatically() {
assertThat(ContentResolver.getMasterSyncAutomatically()).isFalse();
ContentResolver.setMasterSyncAutomatically(true);
assertThat(ContentResolver.getMasterSyncAutomatically()).isTrue();
}
@Test
public void shouldDelegateCallsToRegisteredProvider() {
ShadowContentResolver.registerProviderInternal(AUTHORITY, new ContentProvider() {
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return new BaseCursor();
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return -1;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return -1;
}
@Override
public String getType(Uri uri) {
return null;
}
});
final Uri uri = Uri.parse("content://"+AUTHORITY+"/some/path");
final Uri unrelated = Uri.parse("content://unrelated/some/path");
assertThat(contentResolver.query(uri, null, null, null, null)).isNotNull();
assertThat(contentResolver.insert(uri, new ContentValues())).isNull();
assertThat(contentResolver.delete(uri, null, null)).isEqualTo(-1);
assertThat(contentResolver.update(uri, new ContentValues(), null, null)).isEqualTo(-1);
assertThat(contentResolver.query(unrelated, null, null, null, null)).isNull();
assertThat(contentResolver.insert(unrelated, new ContentValues())).isNotNull();
assertThat(contentResolver.delete(unrelated, null, null)).isEqualTo(1);
assertThat(contentResolver.update(unrelated, new ContentValues(), null, null)).isEqualTo(1);
}
@Test
public void shouldThrowConfiguredExceptionWhenRegisteringContentObservers() {
ShadowContentResolver scr = shadowOf(contentResolver);
scr.setRegisterContentProviderException(EXTERNAL_CONTENT_URI, new SecurityException());
try {
contentResolver.registerContentObserver(
EXTERNAL_CONTENT_URI, true, new TestContentObserver(null));
fail();
} catch (SecurityException expected) {}
}
@Test
public void shouldClearConfiguredExceptionForRegisteringContentObservers() {
ShadowContentResolver scr = shadowOf(contentResolver);
scr.setRegisterContentProviderException(EXTERNAL_CONTENT_URI, new SecurityException());
scr.clearRegisterContentProviderException(EXTERNAL_CONTENT_URI);
// Should not throw the SecurityException.
contentResolver.registerContentObserver(
EXTERNAL_CONTENT_URI, true, new TestContentObserver(null));
}
@Test
public void shouldRegisterContentObservers() {
TestContentObserver co = new TestContentObserver(null);
ShadowContentResolver scr = shadowOf(contentResolver);
assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).isEmpty();
contentResolver.registerContentObserver(EXTERNAL_CONTENT_URI, true, co);
assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).containsExactly((ContentObserver) co);
assertThat(co.changed).isFalse();
contentResolver.notifyChange(EXTERNAL_CONTENT_URI, null);
assertThat(co.changed).isTrue();
contentResolver.unregisterContentObserver(co);
assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).isEmpty();
}
@Test
public void shouldUnregisterContentObservers() {
TestContentObserver co = new TestContentObserver(null);
ShadowContentResolver scr = shadowOf(contentResolver);
contentResolver.registerContentObserver(EXTERNAL_CONTENT_URI, true, co);
assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).contains(co);
contentResolver.unregisterContentObserver(co);
assertThat(scr.getContentObservers(EXTERNAL_CONTENT_URI)).isEmpty();
assertThat(co.changed).isFalse();
contentResolver.notifyChange(EXTERNAL_CONTENT_URI, null);
assertThat(co.changed).isFalse();
}
@Test
public void shouldNotifyChildContentObservers() throws Exception {
TestContentObserver co1 = new TestContentObserver(null);
TestContentObserver co2 = new TestContentObserver(null);
Uri childUri = EXTERNAL_CONTENT_URI.buildUpon().appendPath("path").build();
contentResolver.registerContentObserver(EXTERNAL_CONTENT_URI, true, co1);
contentResolver.registerContentObserver(childUri, false, co2);
co1.changed = co2.changed = false;
contentResolver.notifyChange(childUri, null);
assertThat(co1.changed).isTrue();
assertThat(co2.changed).isTrue();
co1.changed = co2.changed = false;
contentResolver.notifyChange(EXTERNAL_CONTENT_URI, null);
assertThat(co1.changed).isTrue();
assertThat(co2.changed).isFalse();
co1.changed = co2.changed = false;
contentResolver.notifyChange(childUri.buildUpon().appendPath("extra").build(), null);
assertThat(co1.changed).isTrue();
assertThat(co2.changed).isFalse();
}
@Test
public void getProvider_shouldCreateProviderFromManifest() throws Exception {
Uri uri = Uri.parse("content://org.robolectric.authority1/shadows");
ContentProvider provider = ShadowContentResolver.getProvider(uri);
assertThat(provider).isNotNull();
assertThat(provider.getReadPermission()).isEqualTo("READ_PERMISSION");
assertThat(provider.getWritePermission()).isEqualTo("WRITE_PERMISSION");
assertThat(provider.getPathPermissions()).asList().hasSize(1);
// unfortunately, there is no direct way of testing if authority is set or not
// however, it's checked in ContentProvider.Transport method calls (validateIncomingUri), so
// it's the closest we can test against
provider.getIContentProvider().getType(uri); // should not throw
}
@Test
@Config(manifest = NONE)
public void getProvider_shouldNotReturnAnyProviderWhenManifestIsNull() {
Application application = new Application();
shadowOf(application).callAttach(RuntimeEnvironment.systemContext);
assertThat(ShadowContentResolver.getProvider(Uri.parse("content://"))).isNull();
}
@Test
public void openTypedAssetFileDescriptor_shouldOpenDescriptor() throws IOException, RemoteException {
Robolectric.setupContentProvider(MyContentProvider.class, AUTHORITY);
AssetFileDescriptor afd =
contentResolver.openTypedAssetFileDescriptor(
Uri.parse("content://" + AUTHORITY + "/whatever"), "*/*", null);
FileDescriptor descriptor = afd.getFileDescriptor();
assertThat(descriptor).isNotNull();
}
@Test
@Config(minSdk = KITKAT)
public void takeAndReleasePersistableUriPermissions() {
List<UriPermission> permissions = contentResolver.getPersistedUriPermissions();
assertThat(permissions).isEmpty();
// Take the read permission for the uri.
Uri uri = Uri.parse("content://" + AUTHORITY + "/whatever");
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
assertThat(permissions).hasSize(1);
assertThat(permissions.get(0).getUri()).isSameInstanceAs(uri);
assertThat(permissions.get(0).isReadPermission()).isTrue();
assertThat(permissions.get(0).isWritePermission()).isFalse();
// Take the write permission for the uri.
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
assertThat(permissions).hasSize(1);
assertThat(permissions.get(0).getUri()).isSameInstanceAs(uri);
assertThat(permissions.get(0).isReadPermission()).isTrue();
assertThat(permissions.get(0).isWritePermission()).isTrue();
// Release the read permission for the uri.
contentResolver.releasePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
assertThat(permissions).hasSize(1);
assertThat(permissions.get(0).getUri()).isSameInstanceAs(uri);
assertThat(permissions.get(0).isReadPermission()).isFalse();
assertThat(permissions.get(0).isWritePermission()).isTrue();
// Release the write permission for the uri.
contentResolver.releasePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
assertThat(permissions).isEmpty();
}
@Test
public void getSyncAdapterTypes() {
SyncAdapterType[] syncAdapterTypes =
new SyncAdapterType[] {
new SyncAdapterType(
"authority1", "accountType1", /* userVisible=*/ false, /* supportsUploading=*/ false),
new SyncAdapterType(
"authority2", "accountType2", /* userVisible=*/ true, /* supportsUploading=*/ false),
new SyncAdapterType(
"authority3", "accountType3", /* userVisible=*/ true, /* supportsUploading=*/ true)
};
ShadowContentResolver.setSyncAdapterTypes(syncAdapterTypes);
assertThat(ContentResolver.getSyncAdapterTypes()).isEqualTo(syncAdapterTypes);
}
private static class QueryParamTrackingCursor extends BaseCursor {
public Uri uri;
public String[] projection;
public String selection;
public String[] selectionArgs;
public String sortOrder;
@Override
public void setQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
this.uri = uri;
this.projection = projection;
this.selection = selection;
this.selectionArgs = selectionArgs;
this.sortOrder = sortOrder;
}
}
private static class TestContentObserver extends ContentObserver {
public TestContentObserver(Handler handler) {
super(handler);
}
public boolean changed = false;
@Override
public void onChange(boolean selfChange) {
changed = true;
}
@Override
public void onChange(boolean selfChange, Uri uri) {
changed = true;
}
}
/**
* Provider that opens a temporary file.
*/
public static class MyContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
return null;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
return null;
}
@Override
public int delete(Uri uri, String s, String[] strings) {
return 0;
}
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
final File file =
new File(ApplicationProvider.getApplicationContext().getFilesDir(), "test_file");
try {
file.createNewFile();
} catch (IOException e) {
throw new RuntimeException("error creating new file", e);
}
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
}
}