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 
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 import org.mockito.ArgumentMatchers;
42 
43 import java.util.Objects;
44 
45 @RunWith(AndroidJUnit4.class)
46 public class ContentProviderOperationTest {
47     private static final Uri TEST_URI = Uri.parse("content://com.example");
48     private static final Uri TEST_URI_RESULT = Uri.parse("content://com.example/12");
49     private static final String TEST_SELECTION = "foo=?";
50     private static final String[] TEST_SELECTION_ARGS = new String[] { "bar" };
51     private static final String TEST_METHOD = "test_method";
52     private static final String TEST_ARG = "test_arg";
53 
54     private static final ContentValues TEST_VALUES = new ContentValues();
55     private static final Bundle TEST_EXTRAS = new Bundle();
56     private static final Bundle TEST_EXTRAS_WITH_SQL = new Bundle();
57     private static final Bundle TEST_EXTRAS_RESULT = new Bundle();
58 
59     static {
60         TEST_VALUES.put("test_key", "test_value");
61 
62         TEST_EXTRAS.putString("test_key", "test_value");
63 
64         TEST_EXTRAS_WITH_SQL.putAll(TEST_EXTRAS);
TEST_EXTRAS_WITH_SQL.putString(QUERY_ARG_SQL_SELECTION, TEST_SELECTION)65         TEST_EXTRAS_WITH_SQL.putString(QUERY_ARG_SQL_SELECTION, TEST_SELECTION);
TEST_EXTRAS_WITH_SQL.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, TEST_SELECTION_ARGS)66         TEST_EXTRAS_WITH_SQL.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, TEST_SELECTION_ARGS);
67 
68         TEST_EXTRAS_RESULT.putString("test_result", "42");
69     }
70 
71     private static final ContentProviderResult[] TEST_RESULTS = new ContentProviderResult[] {
72             new ContentProviderResult(TEST_URI_RESULT),
73             new ContentProviderResult(84),
74             new ContentProviderResult(TEST_EXTRAS_RESULT),
75             new ContentProviderResult(new IllegalArgumentException()),
76     };
77 
78     private ContentProvider provider;
79 
80     private ContentProviderOperation op;
81     private ContentProviderResult res;
82 
83     @Before
setUp()84     public void setUp() throws Exception {
85         provider = mock(ContentProvider.class);
86     }
87 
88     @Test
testInsert()89     public void testInsert() throws Exception {
90         op = ContentProviderOperation.newInsert(TEST_URI)
91                 .withValues(TEST_VALUES)
92                 .withExtras(TEST_EXTRAS)
93                 .build();
94 
95         assertEquals(TEST_URI, op.getUri());
96         assertTrue(op.isInsert());
97         assertTrue(op.isWriteOperation());
98 
99         when(provider.insert(eq(TEST_URI), eq(TEST_VALUES), eqBundle(TEST_EXTRAS)))
100                 .thenReturn(TEST_URI_RESULT);
101         res = op.apply(provider, null, 0);
102         assertEquals(TEST_URI_RESULT, res.uri);
103     }
104 
105     @Test
testUpdate()106     public void testUpdate() throws Exception {
107         op = ContentProviderOperation.newUpdate(TEST_URI)
108                 .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
109                 .withValues(TEST_VALUES)
110                 .withExtras(TEST_EXTRAS)
111                 .build();
112 
113         assertEquals(TEST_URI, op.getUri());
114         assertTrue(op.isUpdate());
115         assertTrue(op.isWriteOperation());
116 
117         when(provider.update(eq(TEST_URI), eq(TEST_VALUES), eqBundle(TEST_EXTRAS_WITH_SQL)))
118                 .thenReturn(1);
119         res = op.apply(provider, null, 0);
120         assertEquals(1, (int) res.count);
121     }
122 
123     @Test
testDelete()124     public void testDelete() throws Exception {
125         op = ContentProviderOperation.newDelete(TEST_URI)
126                 .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
127                 .withExtras(TEST_EXTRAS)
128                 .build();
129 
130         assertEquals(TEST_URI, op.getUri());
131         assertTrue(op.isDelete());
132         assertTrue(op.isWriteOperation());
133 
134         when(provider.delete(eq(TEST_URI), eqBundle(TEST_EXTRAS_WITH_SQL)))
135                 .thenReturn(1);
136         res = op.apply(provider, null, 0);
137         assertEquals(1, (int) res.count);
138     }
139 
140     @Test
testAssertQuery()141     public void testAssertQuery() throws Exception {
142         op = ContentProviderOperation.newAssertQuery(TEST_URI)
143                 .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
144                 .withExtras(TEST_EXTRAS)
145                 .withValues(TEST_VALUES)
146                 .build();
147 
148         assertEquals(TEST_URI, op.getUri());
149         assertTrue(op.isAssertQuery());
150         assertTrue(op.isReadOperation());
151 
152         final MatrixCursor cursor = new MatrixCursor(new String[] { "test_key" });
153         cursor.addRow(new Object[] { "test_value" });
154 
155         when(provider.query(eq(TEST_URI), eq(new String[] { "test_key" }),
156                 eqBundle(TEST_EXTRAS_WITH_SQL), eq(null)))
157                         .thenReturn(cursor);
158         op.apply(provider, null, 0);
159     }
160 
161     @Test
testCall()162     public void testCall() throws Exception {
163         op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
164                 .withExtras(TEST_EXTRAS)
165                 .build();
166 
167         assertEquals(TEST_URI, op.getUri());
168         assertTrue(op.isCall());
169 
170         when(provider.call(eq(TEST_URI.getAuthority()), eq(TEST_METHOD),
171                 eq(TEST_ARG), eqBundle(TEST_EXTRAS)))
172                         .thenReturn(TEST_EXTRAS_RESULT);
173         res = op.apply(provider, null, 0);
174         assertEquals(TEST_EXTRAS_RESULT, res.extras);
175     }
176 
177     @Test
testBackReferenceSelection()178     public void testBackReferenceSelection() throws Exception {
179         op = ContentProviderOperation.newDelete(TEST_URI)
180                 .withSelection(null, new String[] { "a", "b", "c", "d" })
181                 .withSelectionBackReference(0, 0)
182                 .withSelectionBackReference(1, 1)
183                 .withSelectionBackReference(2, 2, "test_result")
184                 .build();
185 
186         final String[] res = op.resolveSelectionArgsBackReferences(TEST_RESULTS,
187                 TEST_RESULTS.length);
188         assertEquals("12", res[0]);
189         assertEquals("84", res[1]);
190         assertEquals("42", res[2]);
191         assertEquals("d", res[3]);
192     }
193 
194     @Test
testBackReferenceValue()195     public void testBackReferenceValue() throws Exception {
196         final ContentValues values = new ContentValues();
197         values.put("a", "a");
198         values.put("b", "b");
199         values.put("c", "c");
200         values.put("d", "d");
201 
202         op = ContentProviderOperation.newUpdate(TEST_URI)
203                 .withValues(values)
204                 .withValueBackReference("a", 0)
205                 .withValueBackReference("b", 1)
206                 .withValueBackReference("c", 2, "test_result")
207                 .build();
208 
209         final ContentValues res = op.resolveValueBackReferences(TEST_RESULTS,
210                 TEST_RESULTS.length);
211         assertEquals(12L, (long) res.get("a"));
212         assertEquals(84L, (long) res.get("b"));
213         assertEquals("42", res.get("c"));
214         assertEquals("d", res.get("d"));
215     }
216 
217     @Test
testBackReferenceExtra()218     public void testBackReferenceExtra() throws Exception {
219         final Bundle extras = new Bundle();
220         extras.putString("a", "a");
221         extras.putString("b", "b");
222         extras.putString("c", "c");
223         extras.putString("d", "d");
224 
225         op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
226                 .withExtras(extras)
227                 .withExtraBackReference("a", 0)
228                 .withExtraBackReference("b", 1)
229                 .withExtraBackReference("c", 2, "test_result")
230                 .build();
231 
232         final Bundle res = op.resolveExtrasBackReferences(TEST_RESULTS,
233                 TEST_RESULTS.length);
234         assertEquals(12L, (long) res.get("a"));
235         assertEquals(84L, (long) res.get("b"));
236         assertEquals("42", res.get("c"));
237         assertEquals("d", res.get("d"));
238     }
239 
240     @Test
testExceptionAllowed()241     public void testExceptionAllowed() throws Exception {
242         op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
243                 .withExtras(TEST_EXTRAS)
244                 .withExceptionAllowed(true)
245                 .build();
246 
247         assertTrue(op.isExceptionAllowed());
248 
249         when(provider.call(eq(TEST_URI.getAuthority()), eq(TEST_METHOD),
250                 eq(TEST_ARG), eqBundle(TEST_EXTRAS)))
251                         .thenThrow(new IllegalArgumentException());
252         res = op.apply(provider, null, 0);
253         assertTrue((res.exception instanceof IllegalArgumentException));
254     }
255 
256     @Test
testLayering()257     public void testLayering() throws Exception {
258         op = ContentProviderOperation.newAssertQuery(TEST_URI)
259                 .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
260                 .withExtras(TEST_EXTRAS)
261                 .withExtra("test_key", "other_extra")
262                 .withValues(TEST_VALUES)
263                 .withValue("test_key", "other_value")
264                 .build();
265 
266         assertEquals("other_extra", op.resolveExtrasBackReferences(null, 0).getString("test_key"));
267         assertEquals("other_value", op.resolveValueBackReferences(null, 0).getAsString("test_key"));
268     }
269 
eqBundle(Bundle bundle)270     public static Bundle eqBundle(Bundle bundle) {
271         return ArgumentMatchers.argThat((other) -> {
272             // Ideally we'd use something like Bundle.kindofEquals() here, but
273             // it doesn't perform deep equals inside String[] values, so the
274             // best we can do is a simple string equality check
275             return Objects.equals(bundle.toString(), other.toString());
276         });
277     }
278 }
279