1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.contacts;
18 
19 import static com.android.providers.contacts.TestUtils.cv;
20 
21 import com.google.android.collect.Lists;
22 
23 import android.content.ContentProviderOperation;
24 import android.content.ContentValues;
25 import android.provider.ContactsContract;
26 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
27 import android.provider.ContactsContract.Contacts;
28 import android.provider.ContactsContract.Data;
29 import android.provider.ContactsContract.Profile;
30 import android.provider.ContactsContract.RawContacts;
31 import android.test.suitebuilder.annotation.LargeTest;
32 import android.util.Log;
33 
34 import java.util.ArrayList;
35 
36 /**
37  * Tests to make sure we're handling DB transactions properly in regard to two databases,
38  * the profile db and the contacts db.
39  */
40 @LargeTest
41 public class ContactsProvider2TransactionTest extends BaseContactsProvider2Test {
42     private SynchronousContactsProvider2 mProvider;
43 
44     @Override
setUp()45     protected void setUp() throws Exception {
46         super.setUp();
47 
48         mProvider = (SynchronousContactsProvider2) getProvider();
49     }
50 
51     @Override
tearDown()52     protected void tearDown() throws Exception {
53         super.tearDown();
54 
55         mProvider = null;
56     }
57 
58     /**
59      * Make sure we start/finish transactions on the right databases for insert.
60      */
testTransactionCallback_insert()61     public void testTransactionCallback_insert() {
62 
63         final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 86400);
64 
65         // Insert a raw contact.
66         mProvider.resetTrasactionCallbackCalledFlags();
67         mResolver.insert(RawContacts.CONTENT_URI, values);
68 
69         // Make sure we only COMMIT on the contacts DB, but there was no transaction on the
70         // profile db.
71         mProvider.assertCommitTransactionCalledForContactMode();
72         mProvider.assertNoTransactionsForProfileMode();
73 
74 
75         // Insert a profile raw contact.
76         mProvider.resetTrasactionCallbackCalledFlags();
77         mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values);
78 
79         // Even though we only touched the profile DB, we also start and finish a transaction
80         // on the contacts db.  AbstractContactsProvider does that to avoid deadlocks.
81         mProvider.assertCommitTransactionCalledForContactMode();
82         mProvider.assertCommitTransactionCalledForProfileMode();
83     }
84 
85     /**
86      * Make sure we start/finish transactions on the right databases for update.
87      */
testTransactionCallback_update()88     public void testTransactionCallback_update() {
89 
90         final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 86400);
91 
92         // Make sure to create a raw contact and a profile raw contact.
93         mResolver.insert(RawContacts.CONTENT_URI, values);
94         mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values);
95 
96         values.clear();
97         values.put(RawContacts.LAST_TIME_CONTACTED, 86400 * 2);
98 
99         // Update all raw contacts.
100         mProvider.resetTrasactionCallbackCalledFlags();
101         assertTrue(mResolver.update(RawContacts.CONTENT_URI, values, null, null) > 0);
102 
103         // Make sure we only COMMIT on the contacts DB, but there was no transaction on the
104         // profile db.
105         mProvider.assertCommitTransactionCalledForContactMode();
106         mProvider.assertNoTransactionsForProfileMode();
107 
108 
109         // Update all profile raw contacts.
110         mProvider.resetTrasactionCallbackCalledFlags();
111         assertTrue(mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, values, null, null) > 0);
112 
113         // Even though we only touched the profile DB, we also start and finish a transaction
114         // on the contacts db.  AbstractContactsProvider does that to avoid deadlocks.
115         mProvider.assertCommitTransactionCalledForContactMode();
116         mProvider.assertCommitTransactionCalledForProfileMode();
117     }
118 
119     /**
120      * Make sure we start/finish transactions on the right databases for delete.
121      */
testTransactionCallback_delete()122     public void testTransactionCallback_delete() {
123 
124         final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 86400);
125 
126         // Make sure to create a raw contact and a profile raw contact.
127         mResolver.insert(RawContacts.CONTENT_URI, values);
128         mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values);
129 
130         // Delete all raw contacts.
131         mProvider.resetTrasactionCallbackCalledFlags();
132         assertTrue(mResolver.delete(RawContacts.CONTENT_URI, null, null) > 0);
133 
134         // Make sure we only COMMIT on the contacts DB, but there was no transaction on the
135         // profile db.
136         mProvider.assertCommitTransactionCalledForContactMode();
137         mProvider.assertNoTransactionsForProfileMode();
138 
139         // Delete all profile raw contact.
140         mProvider.resetTrasactionCallbackCalledFlags();
141         assertTrue(mResolver.delete(Profile.CONTENT_RAW_CONTACTS_URI, null, null) > 0);
142 
143         // Even though we only touched the profile DB, we also start and finish a transaction
144         // on the contacts db.  AbstractContactsProvider does that to avoid deadlocks.
145         mProvider.assertCommitTransactionCalledForContactMode();
146         mProvider.assertCommitTransactionCalledForProfileMode();
147     }
148     /**
149      * Make sure we start/finish transactions on the right databases for bulk insert.
150      */
testTransactionCallback_bulkInsert()151     public void testTransactionCallback_bulkInsert() {
152 
153         final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 86400);
154 
155         // Insert a raw contact.
156         mProvider.resetTrasactionCallbackCalledFlags();
157         mResolver.bulkInsert(RawContacts.CONTENT_URI, new ContentValues[] {values});
158 
159         // Make sure we only COMMIT on the contacts DB, but there was no transaction on the
160         // profile db.
161         mProvider.assertCommitTransactionCalledForContactMode();
162         mProvider.assertNoTransactionsForProfileMode();
163 
164 
165         // Insert a profile raw contact.
166         mProvider.resetTrasactionCallbackCalledFlags();
167         mResolver.bulkInsert(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[] {values});
168 
169         // Even though we only touched the profile DB, we also start and finish a transaction
170         // on the contacts db.  AbstractContactsProvider does that to avoid deadlocks.
171         mProvider.assertCommitTransactionCalledForContactMode();
172         mProvider.assertCommitTransactionCalledForProfileMode();
173     }
174 
175     /**
176      * Add an operation to create a raw contact.
177      */
addInsertContactOperations(ArrayList<ContentProviderOperation> ops)178     private static void addInsertContactOperations(ArrayList<ContentProviderOperation> ops) {
179         ContentProviderOperation.Builder b;
180         b = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
181         b.withValue(RawContacts.STARRED, 1);
182         b.withValue(RawContacts.LAST_TIME_CONTACTED, 86400 * 21);
183         ops.add(b.build());
184 
185         b = ContentProviderOperation.newInsert(Data.CONTENT_URI);
186         b.withValueBackReference(Data.RAW_CONTACT_ID, ops.size() - 1);
187         b.withValue(StructuredName.DISPLAY_NAME, "Regular Contact");
188         b.withValue(StructuredName.GIVEN_NAME, "Regular");
189         b.withValue(StructuredName.FAMILY_NAME, "Contact");
190         b.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
191         ops.add(b.build());
192     }
193 
194     /**
195      * Check for a contact created that'll be created for {@link #addInsertContactOperations}.
196      */
checkStoredContact()197     private void checkStoredContact() {
198         assertStoredValues(Contacts.CONTENT_URI, cv(
199                 Contacts.DISPLAY_NAME, "Regular Contact",
200                 RawContacts.LAST_TIME_CONTACTED, 0
201                 ));
202     }
203 
204     /**
205      * Add an operation to create a profile raw contact.
206      */
addInsertProfileOperations(ArrayList<ContentProviderOperation> ops)207     private static void addInsertProfileOperations(ArrayList<ContentProviderOperation> ops) {
208         ContentProviderOperation.Builder b;
209         b = ContentProviderOperation.newInsert(Profile.CONTENT_RAW_CONTACTS_URI);
210         b.withValue(RawContacts.STARRED, 1);
211         b.withValue(RawContacts.LAST_TIME_CONTACTED, 86400 * 11);
212         ops.add(b.build());
213 
214         b = ContentProviderOperation.newInsert(Data.CONTENT_URI);
215         b.withValueBackReference(Data.RAW_CONTACT_ID, ops.size() - 1);
216         b.withValue(StructuredName.DISPLAY_NAME, "Profile Contact");
217         b.withValue(StructuredName.GIVEN_NAME, "Profile");
218         b.withValue(StructuredName.FAMILY_NAME, "Contact");
219         b.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
220         ops.add(b.build());
221     }
222 
223     /**
224      * Check for a profile contact created that'll be created for
225      * {@link #addInsertProfileOperations}.
226      */
checkStoredProfile()227     private void checkStoredProfile() {
228         assertStoredValues(Profile.CONTENT_URI, cv(
229                 Contacts.DISPLAY_NAME, "Profile Contact",
230                 RawContacts.LAST_TIME_CONTACTED, 0
231                 ));
232     }
233 
testTransactionCallback_contactBatch()234     public void testTransactionCallback_contactBatch() throws Exception {
235         final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
236 
237         addInsertContactOperations(ops);
238 
239         mProvider.resetTrasactionCallbackCalledFlags();
240 
241         // Execute the operations.
242         mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
243 
244         // Check the result
245         mProvider.assertCommitTransactionCalledForContactMode();
246         mProvider.assertNoTransactionsForProfileMode();
247 
248         checkStoredContact();
249     }
250 
testTransactionCallback_profileBatch()251     public void testTransactionCallback_profileBatch() throws Exception {
252         final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
253 
254         addInsertProfileOperations(ops);
255 
256         mProvider.resetTrasactionCallbackCalledFlags();
257 
258         // Execute the operations.
259         mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
260 
261         // Check the result
262         mProvider.assertCommitTransactionCalledForContactMode();
263         mProvider.assertCommitTransactionCalledForProfileMode();
264 
265         checkStoredProfile();
266     }
267 
testTransactionCallback_mixedBatch()268     public void testTransactionCallback_mixedBatch() throws Exception {
269         final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
270 
271         // Create a raw contact and a profile raw contact in a single batch.
272 
273         addInsertContactOperations(ops);
274         addInsertProfileOperations(ops);
275 
276         mProvider.resetTrasactionCallbackCalledFlags();
277 
278         // Execute the operations.
279         mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
280 
281         // Check the result
282         mProvider.assertCommitTransactionCalledForContactMode();
283         mProvider.assertCommitTransactionCalledForProfileMode();
284 
285         checkStoredProfile();
286         checkStoredContact();
287     }
288 
testTransactionCallback_mixedBatchReversed()289     public void testTransactionCallback_mixedBatchReversed() throws Exception {
290         final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
291 
292         // Create a profile raw contact and a raw contact in a single batch.
293 
294         addInsertProfileOperations(ops);
295         addInsertContactOperations(ops);
296 
297         mProvider.resetTrasactionCallbackCalledFlags();
298 
299         // Execute the operations.
300         mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
301 
302         // Check the result
303         mProvider.assertCommitTransactionCalledForContactMode();
304         mProvider.assertCommitTransactionCalledForProfileMode();
305 
306         checkStoredProfile();
307         checkStoredContact();
308     }
309 }
310