1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package android.content.cts;
18 
19 import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION;
20 import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.when;
27 
28 import android.content.ContentProvider;
29 import android.content.ContentProviderOperation;
30 import android.content.ContentProviderResult;
31 import android.content.ContentValues;
32 import android.database.MatrixCursor;
33 import android.net.Uri;
34 import android.os.Bundle;
35 import android.platform.test.annotations.AppModeSdkSandbox;
36 
37 import androidx.test.runner.AndroidJUnit4;
38 
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.mockito.ArgumentMatchers;
43 
44 import java.util.Objects;
45 
46 @RunWith(AndroidJUnit4.class)
47 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
48 public class ContentProviderOperationTest {
49     private static final Uri TEST_URI = Uri.parse("content://com.example");
50     private static final Uri TEST_URI_RESULT = Uri.parse("content://com.example/12");
51     private static final String TEST_SELECTION = "foo=?";
52     private static final String[] TEST_SELECTION_ARGS = new String[] { "bar" };
53     private static final String TEST_METHOD = "test_method";
54     private static final String TEST_ARG = "test_arg";
55 
56     private static final ContentValues TEST_VALUES = new ContentValues();
57     private static final Bundle TEST_EXTRAS = new Bundle();
58     private static final Bundle TEST_EXTRAS_WITH_SQL = new Bundle();
59     private static final Bundle TEST_EXTRAS_RESULT = new Bundle();
60 
61     static {
62         TEST_VALUES.put("test_key", "test_value");
63 
64         TEST_EXTRAS.putString("test_key", "test_value");
65 
66         TEST_EXTRAS_WITH_SQL.putAll(TEST_EXTRAS);
TEST_EXTRAS_WITH_SQL.putString(QUERY_ARG_SQL_SELECTION, TEST_SELECTION)67         TEST_EXTRAS_WITH_SQL.putString(QUERY_ARG_SQL_SELECTION, TEST_SELECTION);
TEST_EXTRAS_WITH_SQL.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, TEST_SELECTION_ARGS)68         TEST_EXTRAS_WITH_SQL.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, TEST_SELECTION_ARGS);
69 
70         TEST_EXTRAS_RESULT.putString("test_result", "42");
71     }
72 
73     private static final ContentProviderResult[] TEST_RESULTS = new ContentProviderResult[] {
74             new ContentProviderResult(TEST_URI_RESULT),
75             new ContentProviderResult(84),
76             new ContentProviderResult(TEST_EXTRAS_RESULT),
77             new ContentProviderResult(new IllegalArgumentException()),
78     };
79 
80     private ContentProvider provider;
81 
82     private ContentProviderOperation op;
83     private ContentProviderResult res;
84 
85     @Before
setUp()86     public void setUp() throws Exception {
87         provider = mock(ContentProvider.class);
88     }
89 
90     @Test
testInsert()91     public void testInsert() throws Exception {
92         op = ContentProviderOperation.newInsert(TEST_URI)
93                 .withValues(TEST_VALUES)
94                 .withExtras(TEST_EXTRAS)
95                 .build();
96 
97         assertEquals(TEST_URI, op.getUri());
98         assertTrue(op.isInsert());
99         assertTrue(op.isWriteOperation());
100 
101         when(provider.insert(eq(TEST_URI), eq(TEST_VALUES), eqBundle(TEST_EXTRAS)))
102                 .thenReturn(TEST_URI_RESULT);
103         res = op.apply(provider, null, 0);
104         assertEquals(TEST_URI_RESULT, res.uri);
105     }
106 
107     @Test
testUpdate()108     public void testUpdate() throws Exception {
109         op = ContentProviderOperation.newUpdate(TEST_URI)
110                 .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
111                 .withValues(TEST_VALUES)
112                 .withExtras(TEST_EXTRAS)
113                 .build();
114 
115         assertEquals(TEST_URI, op.getUri());
116         assertTrue(op.isUpdate());
117         assertTrue(op.isWriteOperation());
118 
119         when(provider.update(eq(TEST_URI), eq(TEST_VALUES), eqBundle(TEST_EXTRAS_WITH_SQL)))
120                 .thenReturn(1);
121         res = op.apply(provider, null, 0);
122         assertEquals(1, (int) res.count);
123     }
124 
125     @Test
testDelete()126     public void testDelete() throws Exception {
127         op = ContentProviderOperation.newDelete(TEST_URI)
128                 .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
129                 .withExtras(TEST_EXTRAS)
130                 .build();
131 
132         assertEquals(TEST_URI, op.getUri());
133         assertTrue(op.isDelete());
134         assertTrue(op.isWriteOperation());
135 
136         when(provider.delete(eq(TEST_URI), eqBundle(TEST_EXTRAS_WITH_SQL)))
137                 .thenReturn(1);
138         res = op.apply(provider, null, 0);
139         assertEquals(1, (int) res.count);
140     }
141 
142     @Test
testAssertQuery()143     public void testAssertQuery() throws Exception {
144         op = ContentProviderOperation.newAssertQuery(TEST_URI)
145                 .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
146                 .withExtras(TEST_EXTRAS)
147                 .withValues(TEST_VALUES)
148                 .build();
149 
150         assertEquals(TEST_URI, op.getUri());
151         assertTrue(op.isAssertQuery());
152         assertTrue(op.isReadOperation());
153 
154         final MatrixCursor cursor = new MatrixCursor(new String[] { "test_key" });
155         cursor.addRow(new Object[] { "test_value" });
156 
157         when(provider.query(eq(TEST_URI), eq(new String[] { "test_key" }),
158                 eqBundle(TEST_EXTRAS_WITH_SQL), eq(null)))
159                         .thenReturn(cursor);
160         op.apply(provider, null, 0);
161     }
162 
163     @Test
testCall()164     public void testCall() throws Exception {
165         op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
166                 .withExtras(TEST_EXTRAS)
167                 .build();
168 
169         assertEquals(TEST_URI, op.getUri());
170         assertTrue(op.isCall());
171 
172         when(provider.call(eq(TEST_URI.getAuthority()), eq(TEST_METHOD),
173                 eq(TEST_ARG), eqBundle(TEST_EXTRAS)))
174                         .thenReturn(TEST_EXTRAS_RESULT);
175         res = op.apply(provider, null, 0);
176         assertEquals(TEST_EXTRAS_RESULT, res.extras);
177     }
178 
179     @Test
testBackReferenceSelection()180     public void testBackReferenceSelection() throws Exception {
181         op = ContentProviderOperation.newDelete(TEST_URI)
182                 .withSelection(null, new String[] { "a", "b", "c", "d" })
183                 .withSelectionBackReference(0, 0)
184                 .withSelectionBackReference(1, 1)
185                 .withSelectionBackReference(2, 2, "test_result")
186                 .build();
187 
188         final String[] res = op.resolveSelectionArgsBackReferences(TEST_RESULTS,
189                 TEST_RESULTS.length);
190         assertEquals("12", res[0]);
191         assertEquals("84", res[1]);
192         assertEquals("42", res[2]);
193         assertEquals("d", res[3]);
194     }
195 
196     @Test
testBackReferenceValue()197     public void testBackReferenceValue() throws Exception {
198         final ContentValues values = new ContentValues();
199         values.put("a", "a");
200         values.put("b", "b");
201         values.put("c", "c");
202         values.put("d", "d");
203 
204         op = ContentProviderOperation.newUpdate(TEST_URI)
205                 .withValues(values)
206                 .withValueBackReference("a", 0)
207                 .withValueBackReference("b", 1)
208                 .withValueBackReference("c", 2, "test_result")
209                 .build();
210 
211         final ContentValues res = op.resolveValueBackReferences(TEST_RESULTS,
212                 TEST_RESULTS.length);
213         assertEquals(12L, (long) res.get("a"));
214         assertEquals(84L, (long) res.get("b"));
215         assertEquals("42", res.get("c"));
216         assertEquals("d", res.get("d"));
217     }
218 
219     @Test
testBackReferenceExtra()220     public void testBackReferenceExtra() throws Exception {
221         final Bundle extras = new Bundle();
222         extras.putString("a", "a");
223         extras.putString("b", "b");
224         extras.putString("c", "c");
225         extras.putString("d", "d");
226 
227         op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
228                 .withExtras(extras)
229                 .withExtraBackReference("a", 0)
230                 .withExtraBackReference("b", 1)
231                 .withExtraBackReference("c", 2, "test_result")
232                 .build();
233 
234         final Bundle res = op.resolveExtrasBackReferences(TEST_RESULTS,
235                 TEST_RESULTS.length);
236         assertEquals(12L, (long) res.get("a"));
237         assertEquals(84L, (long) res.get("b"));
238         assertEquals("42", res.get("c"));
239         assertEquals("d", res.get("d"));
240     }
241 
242     @Test
testExceptionAllowed()243     public void testExceptionAllowed() throws Exception {
244         op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
245                 .withExtras(TEST_EXTRAS)
246                 .withExceptionAllowed(true)
247                 .build();
248 
249         assertTrue(op.isExceptionAllowed());
250 
251         when(provider.call(eq(TEST_URI.getAuthority()), eq(TEST_METHOD),
252                 eq(TEST_ARG), eqBundle(TEST_EXTRAS)))
253                         .thenThrow(new IllegalArgumentException());
254         res = op.apply(provider, null, 0);
255         assertTrue((res.exception instanceof IllegalArgumentException));
256     }
257 
258     @Test
testLayering()259     public void testLayering() throws Exception {
260         op = ContentProviderOperation.newAssertQuery(TEST_URI)
261                 .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
262                 .withExtras(TEST_EXTRAS)
263                 .withExtra("test_key", "other_extra")
264                 .withValues(TEST_VALUES)
265                 .withValue("test_key", "other_value")
266                 .build();
267 
268         assertEquals("other_extra", op.resolveExtrasBackReferences(null, 0).getString("test_key"));
269         assertEquals("other_value", op.resolveValueBackReferences(null, 0).getAsString("test_key"));
270     }
271 
eqBundle(Bundle bundle)272     public static Bundle eqBundle(Bundle bundle) {
273         return ArgumentMatchers.argThat((other) -> {
274             // Ideally we'd use something like Bundle.kindofEquals() here, but
275             // it doesn't perform deep equals inside String[] values, so the
276             // best we can do is a simple string equality check
277             return Objects.equals(bundle.toString(), other.toString());
278         });
279     }
280 }
281