1package com.xtremelabs.robolectric.shadows;
2
3import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
4import static com.xtremelabs.robolectric.Robolectric.shadowOf;
5import static org.hamcrest.CoreMatchers.equalTo;
6import static org.hamcrest.CoreMatchers.hasItem;
7import static org.hamcrest.CoreMatchers.is;
8import static org.hamcrest.CoreMatchers.notNullValue;
9import static org.hamcrest.CoreMatchers.nullValue;
10import static org.hamcrest.CoreMatchers.sameInstance;
11import static org.junit.Assert.*;
12
13import java.io.InputStream;
14import java.io.OutputStream;
15import java.util.ArrayList;
16import java.util.List;
17
18import android.accounts.Account;
19import android.content.*;
20import android.os.Bundle;
21import org.hamcrest.CoreMatchers;
22import org.junit.Before;
23import org.junit.Test;
24import org.junit.runner.RunWith;
25
26import android.app.Activity;
27import android.database.Cursor;
28import android.net.Uri;
29import android.os.RemoteException;
30
31import com.xtremelabs.robolectric.WithTestDefaultsRunner;
32import com.xtremelabs.robolectric.tester.android.database.TestCursor;
33
34@RunWith(WithTestDefaultsRunner.class)
35public class ContentResolverTest {
36    static final String AUTHORITY = "com.xtremelabs.robolectric";
37
38    private ContentResolver contentResolver;
39    private ShadowContentResolver shadowContentResolver;
40    private Uri uri21;
41    private Uri uri22;
42    private Account a, b;
43
44    @Before
45    public void setUp() throws Exception {
46        contentResolver = new Activity().getContentResolver();
47        shadowContentResolver = shadowOf(contentResolver);
48        uri21 = Uri.parse(EXTERNAL_CONTENT_URI.toString() + "/21");
49        uri22 = Uri.parse(EXTERNAL_CONTENT_URI.toString() + "/22");
50
51        a = new Account("a", "type");
52        b = new Account("b", "type");
53    }
54
55    @Test
56    public void insert_shouldReturnIncreasingUris() throws Exception {
57        shadowContentResolver.setNextDatabaseIdForInserts(21);
58
59        assertThat(contentResolver.insert(EXTERNAL_CONTENT_URI, new ContentValues()), equalTo(uri21));
60        assertThat(contentResolver.insert(EXTERNAL_CONTENT_URI, new ContentValues()), equalTo(uri22));
61    }
62
63    @Test
64    public void insert_shouldTrackInsertStatements() throws Exception {
65        ContentValues contentValues = new ContentValues();
66        contentValues.put("foo", "bar");
67        contentResolver.insert(EXTERNAL_CONTENT_URI, contentValues);
68        assertThat(shadowContentResolver.getInsertStatements().size(), is(1));
69        assertThat(shadowContentResolver.getInsertStatements().get(0).getUri(), equalTo(EXTERNAL_CONTENT_URI));
70        assertThat(shadowContentResolver.getInsertStatements().get(0).getContentValues().getAsString("foo"), equalTo("bar"));
71
72        contentValues = new ContentValues();
73        contentValues.put("hello", "world");
74        contentResolver.insert(EXTERNAL_CONTENT_URI, contentValues);
75        assertThat(shadowContentResolver.getInsertStatements().size(), is(2));
76        assertThat(shadowContentResolver.getInsertStatements().get(1).getContentValues().getAsString("hello"), equalTo("world"));
77    }
78
79    @Test
80    public void insert_shouldTrackUpdateStatements() throws Exception {
81        ContentValues contentValues = new ContentValues();
82        contentValues.put("foo", "bar");
83        contentResolver.update(EXTERNAL_CONTENT_URI, contentValues, "robolectric", new String[] { "awesome" });
84        assertThat(shadowContentResolver.getUpdateStatements().size(), is(1));
85        assertThat(shadowContentResolver.getUpdateStatements().get(0).getUri(), equalTo(EXTERNAL_CONTENT_URI));
86        assertThat(shadowContentResolver.getUpdateStatements().get(0).getContentValues().getAsString("foo"), equalTo("bar"));
87        assertThat(shadowContentResolver.getUpdateStatements().get(0).getWhere(), equalTo("robolectric"));
88        assertThat(shadowContentResolver.getUpdateStatements().get(0).getSelectionArgs(), equalTo(new String[] { "awesome" }));
89
90        contentValues = new ContentValues();
91        contentValues.put("hello", "world");
92        contentResolver.update(EXTERNAL_CONTENT_URI, contentValues, null, null);
93        assertThat(shadowContentResolver.getUpdateStatements().size(), is(2));
94        assertThat(shadowContentResolver.getUpdateStatements().get(1).getUri(), equalTo(EXTERNAL_CONTENT_URI));
95        assertThat(shadowContentResolver.getUpdateStatements().get(1).getContentValues().getAsString("hello"), equalTo("world"));
96        assertThat(shadowContentResolver.getUpdateStatements().get(1).getWhere(), nullValue());
97        assertThat(shadowContentResolver.getUpdateStatements().get(1).getSelectionArgs(), nullValue());
98    }
99
100    @Test
101    public void delete_shouldTrackDeletedUris() throws Exception {
102        assertThat(shadowContentResolver.getDeletedUris().size(), equalTo(0));
103
104        assertThat(contentResolver.delete(uri21, null, null), equalTo(1));
105        assertThat(shadowContentResolver.getDeletedUris(), hasItem(uri21));
106        assertThat(shadowContentResolver.getDeletedUris().size(), equalTo(1));
107
108        assertThat(contentResolver.delete(uri22, null, null), equalTo(1));
109        assertThat(shadowContentResolver.getDeletedUris(), hasItem(uri22));
110        assertThat(shadowContentResolver.getDeletedUris().size(), equalTo(2));
111    }
112
113    @Test
114    public void delete_shouldTrackDeletedStatements() {
115        assertThat(shadowContentResolver.getDeleteStatements().size(), equalTo(0));
116
117        assertThat(contentResolver.delete(uri21, "id", new String[] { "5" }), equalTo(1));
118        assertThat(shadowContentResolver.getDeleteStatements().size(), equalTo(1));
119        assertThat(shadowContentResolver.getDeleteStatements().get(0).getUri(), equalTo(uri21));
120        assertThat(shadowContentResolver.getDeleteStatements().get(0).getWhere(), equalTo("id"));
121        assertThat(shadowContentResolver.getDeleteStatements().get(0).getSelectionArgs()[0], equalTo("5"));
122
123        assertThat(contentResolver.delete(uri21, "foo", new String[] { "bar" }), equalTo(1));
124        assertThat(shadowContentResolver.getDeleteStatements().size(), equalTo(2));
125        assertThat(shadowContentResolver.getDeleteStatements().get(1).getUri(), equalTo(uri21));
126        assertThat(shadowContentResolver.getDeleteStatements().get(1).getWhere(), equalTo("foo"));
127        assertThat(shadowContentResolver.getDeleteStatements().get(1).getSelectionArgs()[0], equalTo("bar"));
128    }
129
130    @Test
131    public void query_shouldReturnTheCursorThatWasSet() throws Exception {
132        assertNull(shadowContentResolver.query(null, null, null, null, null));
133        TestCursor cursor = new TestCursor();
134        shadowContentResolver.setCursor(cursor);
135        assertThat((TestCursor) shadowContentResolver.query(null, null, null, null, null),
136                sameInstance(cursor));
137    }
138
139    @Test
140    public void query__shouldReturnSpecificCursorsForSpecificUris() throws Exception {
141        assertNull(shadowContentResolver.query(uri21, null, null, null, null));
142        assertNull(shadowContentResolver.query(uri22, null, null, null, null));
143
144        TestCursor cursor21 = new TestCursor();
145        TestCursor cursor22 = new TestCursor();
146        shadowContentResolver.setCursor(uri21, cursor21);
147        shadowContentResolver.setCursor(uri22, cursor22);
148
149        assertThat((TestCursor) shadowContentResolver.query(uri21, null, null, null, null),
150                sameInstance(cursor21));
151        assertThat((TestCursor) shadowContentResolver.query(uri22, null, null, null, null),
152                sameInstance(cursor22));
153    }
154
155    @Test
156    public void query__shouldKnowWhatItsParamsWere() throws Exception {
157        String[] projection = {};
158        String selection = "select";
159        String[] selectionArgs = {};
160        String sortOrder = "order";
161
162        QueryParamTrackingTestCursor testCursor = new QueryParamTrackingTestCursor();
163
164        shadowContentResolver.setCursor(testCursor);
165        Cursor cursor = shadowContentResolver.query(uri21, projection, selection, selectionArgs, sortOrder);
166        assertThat((QueryParamTrackingTestCursor)cursor, equalTo(testCursor));
167        assertThat(testCursor.uri, equalTo(uri21));
168        assertThat(testCursor.projection, equalTo(projection));
169        assertThat(testCursor.selection, equalTo(selection));
170        assertThat(testCursor.selectionArgs, equalTo(selectionArgs));
171        assertThat(testCursor.sortOrder, equalTo(sortOrder));
172    }
173
174    @Test
175    public void openInputStream_shouldReturnAnInputStream() throws Exception {
176        assertThat(contentResolver.openInputStream(uri21), CoreMatchers.instanceOf(InputStream.class));
177    }
178
179    @Test
180    public void openOutputStream_shouldReturnAnOutputStream() throws Exception {
181        assertThat(contentResolver.openOutputStream(uri21), CoreMatchers.instanceOf(OutputStream.class));
182    }
183
184    @Test
185    public void shouldTrackNotifiedUris() throws Exception {
186        contentResolver.notifyChange(Uri.parse("foo"), null, true);
187        contentResolver.notifyChange(Uri.parse("bar"), null);
188
189        assertThat(shadowContentResolver.getNotifiedUris().size(), equalTo(2));
190        ShadowContentResolver.NotifiedUri uri = shadowContentResolver.getNotifiedUris().get(0);
191
192        assertThat(uri.uri.toString(), equalTo("foo"));
193        assertTrue(uri.syncToNetwork);
194        assertNull(uri.observer);
195
196        uri = shadowContentResolver.getNotifiedUris().get(1);
197
198        assertThat(uri.uri.toString(), equalTo("bar"));
199        assertFalse(uri.syncToNetwork);
200        assertNull(uri.observer);
201    }
202
203    @Test
204    public void applyBatch() throws RemoteException, OperationApplicationException {
205        ArrayList<ContentProviderOperation> resultOperations = shadowContentResolver.getContentProviderOperations(AUTHORITY);
206        assertThat(resultOperations, notNullValue());
207        assertThat(resultOperations.size(), is(0));
208
209        ContentProviderResult[] contentProviderResults = new ContentProviderResult[] {
210                new ContentProviderResult(1),
211                new ContentProviderResult(1),
212        };
213        shadowContentResolver.setContentProviderResult(contentProviderResults);
214        Uri uri = Uri.parse("content://com.xtremelabs.robolectric");
215        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
216        operations.add(ContentProviderOperation.newInsert(uri)
217                .withValue("column1", "foo")
218                .withValue("column2", 5)
219                .build());
220        operations.add(ContentProviderOperation.newUpdate(uri)
221                .withSelection("id_column", new String[] { "99" })
222                .withValue("column1", "bar")
223                .build());
224        operations.add(ContentProviderOperation.newDelete(uri)
225                .withSelection("id_column", new String[] { "11" })
226                .build());
227        ContentProviderResult[] result = contentResolver.applyBatch(AUTHORITY, operations);
228
229        resultOperations = shadowContentResolver.getContentProviderOperations(AUTHORITY);
230        assertThat(resultOperations, equalTo(operations));
231        assertThat(result, equalTo(contentProviderResults));
232    }
233
234    @Test
235    public void shouldKeepTrackOfSyncRequests() throws Exception {
236        ShadowContentResolver.Status status = ShadowContentResolver.getStatus(a, AUTHORITY, true);
237        assertNotNull(status);
238        assertThat(status.syncRequests, equalTo(0));
239        ContentResolver.requestSync(a, AUTHORITY, new Bundle());
240        assertThat(status.syncRequests, equalTo(1));
241        assertNotNull(status.syncExtras);
242    }
243
244    @Test
245    public void shouldSetIsSyncable() throws Exception {
246        assertThat(ContentResolver.getIsSyncable(a, AUTHORITY), equalTo(-1));
247        assertThat(ContentResolver.getIsSyncable(b, AUTHORITY), equalTo(-1));
248        ContentResolver.setIsSyncable(a, AUTHORITY, 1);
249        ContentResolver.setIsSyncable(b, AUTHORITY, 2);
250        assertThat(ContentResolver.getIsSyncable(a, AUTHORITY), equalTo(1));
251        assertThat(ContentResolver.getIsSyncable(b, AUTHORITY), equalTo(2));
252    }
253
254    @Test
255    public void shouldSetSyncAutomatically() throws Exception {
256        assertFalse(ContentResolver.getSyncAutomatically(a, AUTHORITY));
257        ContentResolver.setSyncAutomatically(a, AUTHORITY, true);
258        assertTrue(ContentResolver.getSyncAutomatically(a, AUTHORITY));
259    }
260
261    @Test
262    public void shouldAddPeriodicSync() throws Exception {
263        ContentResolver.addPeriodicSync(a, AUTHORITY, new Bundle(), 6000l);
264        ShadowContentResolver.Status status = ShadowContentResolver.getStatus(a, AUTHORITY);
265        assertNotNull(status);
266        assertThat(status.syncs.size(), is(1));
267        assertThat(status.syncs.get(0).period, is(6000l));
268        assertNotNull(status.syncs.get(0).extras);
269    }
270
271    @Test
272    public void shouldRemovePeriodSync() throws Exception {
273        ContentResolver.addPeriodicSync(a, AUTHORITY, new Bundle(), 6000l);
274        ContentResolver.removePeriodicSync(a, AUTHORITY, new Bundle());
275        assertThat(ShadowContentResolver.getStatus(a, AUTHORITY).syncs.size(), is(0));
276    }
277
278    @Test
279    public void shouldGetPeriodSyncs() throws Exception {
280        assertThat(ContentResolver.getPeriodicSyncs(a, AUTHORITY).size(), is(0));
281        ContentResolver.addPeriodicSync(a, AUTHORITY, new Bundle(), 6000l);
282
283        List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(a, AUTHORITY);
284        assertThat(syncs.size(), is(1));
285
286        PeriodicSync first = syncs.get(0);
287        assertThat(first.account, equalTo(a));
288        assertThat(first.authority, equalTo(AUTHORITY));
289        assertThat(first.period, equalTo(6000l));
290        assertNotNull(first.extras);
291    }
292
293    @Test
294    public void shouldValidateSyncExtras() throws Exception {
295        Bundle bundle = new Bundle();
296        bundle.putString("foo", "strings");
297        bundle.putLong("long", 10l);
298        bundle.putDouble("double", 10.0d);
299        bundle.putFloat("float", 10.0f);
300        bundle.putInt("int", 10);
301        bundle.putParcelable("account", a);
302        ContentResolver.validateSyncExtrasBundle(bundle);
303    }
304
305    @Test(expected = IllegalArgumentException.class)
306    public void shouldValidateSyncExtrasAndThrow() throws Exception {
307        Bundle bundle = new Bundle();
308        bundle.putParcelable("intent", new Intent());
309        ContentResolver.validateSyncExtrasBundle(bundle);
310    }
311
312    @Test
313    public void shouldSetMasterSyncAutomatically() throws Exception {
314        assertFalse(ContentResolver.getMasterSyncAutomatically());
315        ContentResolver.setMasterSyncAutomatically(true);
316        assertTrue(ContentResolver.getMasterSyncAutomatically());
317    }
318
319    @Test
320    public void shouldDelegateCallsToRegisteredProvider() throws Exception {
321        ShadowContentResolver.registerProvider(AUTHORITY, new ContentProvider() {
322            @Override public boolean onCreate() {
323                return false;
324            }
325            @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
326                return new TestCursor();
327            }
328            @Override public Uri insert(Uri uri, ContentValues values) {
329                return null;
330            }
331            @Override public int delete(Uri uri, String selection, String[] selectionArgs) {
332                return -1;
333            }
334            @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
335                return -1;
336            }
337            @Override public String getType(Uri uri) {
338                return null;
339            }
340        });
341        final Uri uri = Uri.parse("content://"+AUTHORITY+"/some/path");
342        final Uri unrelated = Uri.parse("content://unrelated/some/path");
343
344        assertNotNull(contentResolver.query(uri, null, null, null, null));
345        assertNull(contentResolver.insert(uri, new ContentValues()));
346        assertThat(contentResolver.delete(uri, null, null), is(-1));
347        assertThat(contentResolver.update(uri, new ContentValues(), null, null), is(-1));
348
349        assertNull(contentResolver.query(unrelated, null, null, null, null));
350        assertNotNull(contentResolver.insert(unrelated, new ContentValues()));
351        assertThat(contentResolver.delete(unrelated, null, null), is(1));
352        assertThat(contentResolver.update(unrelated, new ContentValues(), null, null), is(0));
353    }
354
355    static class QueryParamTrackingTestCursor extends TestCursor {
356        public Uri uri;
357        public String[] projection;
358        public String selection;
359        public String[] selectionArgs;
360        public String sortOrder;
361
362        @Override
363        public void setQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
364            this.uri = uri;
365            this.projection = projection;
366            this.selection = selection;
367            this.selectionArgs = selectionArgs;
368            this.sortOrder = sortOrder;
369        }
370    }
371}
372