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
17package com.android.providers.contacts;
18
19import static com.android.providers.contacts.TestUtils.cv;
20
21import com.google.android.collect.Lists;
22
23import android.content.ContentProviderOperation;
24import android.content.ContentValues;
25import android.provider.ContactsContract;
26import android.provider.ContactsContract.CommonDataKinds.StructuredName;
27import android.provider.ContactsContract.Contacts;
28import android.provider.ContactsContract.Data;
29import android.provider.ContactsContract.Profile;
30import android.provider.ContactsContract.RawContacts;
31import android.test.suitebuilder.annotation.LargeTest;
32import android.util.Log;
33
34import 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
41public class ContactsProvider2TransactionTest extends BaseContactsProvider2Test {
42    private SynchronousContactsProvider2 mProvider;
43
44    @Override
45    protected void setUp() throws Exception {
46        super.setUp();
47
48        mProvider = (SynchronousContactsProvider2) getProvider();
49    }
50
51    @Override
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     */
61    public void testTransactionCallback_insert() {
62
63        final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 12345);
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     */
88    public void testTransactionCallback_update() {
89
90        final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 12345);
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, 99999);
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     */
122    public void testTransactionCallback_delete() {
123
124        final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 12345);
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     */
151    public void testTransactionCallback_bulkInsert() {
152
153        final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 12345);
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     */
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.TIMES_CONTACTED, 200001);
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     */
197    private void checkStoredContact() {
198        assertStoredValues(Contacts.CONTENT_URI, cv(
199                Contacts.DISPLAY_NAME, "Regular Contact",
200                RawContacts.TIMES_CONTACTED, 200001
201                ));
202    }
203
204    /**
205     * Add an operation to create a profile raw contact.
206     */
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.TIMES_CONTACTED, 100001);
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     */
227    private void checkStoredProfile() {
228        assertStoredValues(Profile.CONTENT_URI, cv(
229                Contacts.DISPLAY_NAME, "Profile Contact",
230                RawContacts.TIMES_CONTACTED, 100001
231                ));
232    }
233
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
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
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
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