| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.providers.contacts; |
| |
| import static com.android.providers.contacts.TestUtils.cv; |
| |
| import com.google.android.collect.Lists; |
| |
| import android.content.ContentProviderOperation; |
| import android.content.ContentValues; |
| import android.provider.ContactsContract; |
| import android.provider.ContactsContract.CommonDataKinds.StructuredName; |
| import android.provider.ContactsContract.Contacts; |
| import android.provider.ContactsContract.Data; |
| import android.provider.ContactsContract.Profile; |
| import android.provider.ContactsContract.RawContacts; |
| import android.test.suitebuilder.annotation.LargeTest; |
| import android.util.Log; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Tests to make sure we're handling DB transactions properly in regard to two databases, |
| * the profile db and the contacts db. |
| */ |
| @LargeTest |
| public class ContactsProvider2TransactionTest extends BaseContactsProvider2Test { |
| private SynchronousContactsProvider2 mProvider; |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| |
| mProvider = (SynchronousContactsProvider2) getProvider(); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| super.tearDown(); |
| |
| mProvider = null; |
| } |
| |
| /** |
| * Make sure we start/finish transactions on the right databases for insert. |
| */ |
| public void testTransactionCallback_insert() { |
| |
| final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 86400); |
| |
| // Insert a raw contact. |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| mResolver.insert(RawContacts.CONTENT_URI, values); |
| |
| // Make sure we only COMMIT on the contacts DB, but there was no transaction on the |
| // profile db. |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertNoTransactionsForProfileMode(); |
| |
| |
| // Insert a profile raw contact. |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values); |
| |
| // Even though we only touched the profile DB, we also start and finish a transaction |
| // on the contacts db. AbstractContactsProvider does that to avoid deadlocks. |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertCommitTransactionCalledForProfileMode(); |
| } |
| |
| /** |
| * Make sure we start/finish transactions on the right databases for update. |
| */ |
| public void testTransactionCallback_update() { |
| |
| final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 86400); |
| |
| // Make sure to create a raw contact and a profile raw contact. |
| mResolver.insert(RawContacts.CONTENT_URI, values); |
| mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values); |
| |
| values.clear(); |
| values.put(RawContacts.LAST_TIME_CONTACTED, 86400 * 2); |
| |
| // Update all raw contacts. |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| assertTrue(mResolver.update(RawContacts.CONTENT_URI, values, null, null) > 0); |
| |
| // Make sure we only COMMIT on the contacts DB, but there was no transaction on the |
| // profile db. |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertNoTransactionsForProfileMode(); |
| |
| |
| // Update all profile raw contacts. |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| assertTrue(mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, values, null, null) > 0); |
| |
| // Even though we only touched the profile DB, we also start and finish a transaction |
| // on the contacts db. AbstractContactsProvider does that to avoid deadlocks. |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertCommitTransactionCalledForProfileMode(); |
| } |
| |
| /** |
| * Make sure we start/finish transactions on the right databases for delete. |
| */ |
| public void testTransactionCallback_delete() { |
| |
| final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 86400); |
| |
| // Make sure to create a raw contact and a profile raw contact. |
| mResolver.insert(RawContacts.CONTENT_URI, values); |
| mResolver.insert(Profile.CONTENT_RAW_CONTACTS_URI, values); |
| |
| // Delete all raw contacts. |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| assertTrue(mResolver.delete(RawContacts.CONTENT_URI, null, null) > 0); |
| |
| // Make sure we only COMMIT on the contacts DB, but there was no transaction on the |
| // profile db. |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertNoTransactionsForProfileMode(); |
| |
| // Delete all profile raw contact. |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| assertTrue(mResolver.delete(Profile.CONTENT_RAW_CONTACTS_URI, null, null) > 0); |
| |
| // Even though we only touched the profile DB, we also start and finish a transaction |
| // on the contacts db. AbstractContactsProvider does that to avoid deadlocks. |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertCommitTransactionCalledForProfileMode(); |
| } |
| /** |
| * Make sure we start/finish transactions on the right databases for bulk insert. |
| */ |
| public void testTransactionCallback_bulkInsert() { |
| |
| final ContentValues values = cv(RawContacts.LAST_TIME_CONTACTED, 86400); |
| |
| // Insert a raw contact. |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| mResolver.bulkInsert(RawContacts.CONTENT_URI, new ContentValues[] {values}); |
| |
| // Make sure we only COMMIT on the contacts DB, but there was no transaction on the |
| // profile db. |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertNoTransactionsForProfileMode(); |
| |
| |
| // Insert a profile raw contact. |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| mResolver.bulkInsert(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[] {values}); |
| |
| // Even though we only touched the profile DB, we also start and finish a transaction |
| // on the contacts db. AbstractContactsProvider does that to avoid deadlocks. |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertCommitTransactionCalledForProfileMode(); |
| } |
| |
| /** |
| * Add an operation to create a raw contact. |
| */ |
| private static void addInsertContactOperations(ArrayList<ContentProviderOperation> ops) { |
| ContentProviderOperation.Builder b; |
| b = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI); |
| b.withValue(RawContacts.STARRED, 1); |
| b.withValue(RawContacts.LAST_TIME_CONTACTED, 86400 * 21); |
| ops.add(b.build()); |
| |
| b = ContentProviderOperation.newInsert(Data.CONTENT_URI); |
| b.withValueBackReference(Data.RAW_CONTACT_ID, ops.size() - 1); |
| b.withValue(StructuredName.DISPLAY_NAME, "Regular Contact"); |
| b.withValue(StructuredName.GIVEN_NAME, "Regular"); |
| b.withValue(StructuredName.FAMILY_NAME, "Contact"); |
| b.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); |
| ops.add(b.build()); |
| } |
| |
| /** |
| * Check for a contact created that'll be created for {@link #addInsertContactOperations}. |
| */ |
| private void checkStoredContact() { |
| assertStoredValues(Contacts.CONTENT_URI, cv( |
| Contacts.DISPLAY_NAME, "Regular Contact", |
| RawContacts.LAST_TIME_CONTACTED, 0 |
| )); |
| } |
| |
| /** |
| * Add an operation to create a profile raw contact. |
| */ |
| private static void addInsertProfileOperations(ArrayList<ContentProviderOperation> ops) { |
| ContentProviderOperation.Builder b; |
| b = ContentProviderOperation.newInsert(Profile.CONTENT_RAW_CONTACTS_URI); |
| b.withValue(RawContacts.STARRED, 1); |
| b.withValue(RawContacts.LAST_TIME_CONTACTED, 86400 * 11); |
| ops.add(b.build()); |
| |
| b = ContentProviderOperation.newInsert(Data.CONTENT_URI); |
| b.withValueBackReference(Data.RAW_CONTACT_ID, ops.size() - 1); |
| b.withValue(StructuredName.DISPLAY_NAME, "Profile Contact"); |
| b.withValue(StructuredName.GIVEN_NAME, "Profile"); |
| b.withValue(StructuredName.FAMILY_NAME, "Contact"); |
| b.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); |
| ops.add(b.build()); |
| } |
| |
| /** |
| * Check for a profile contact created that'll be created for |
| * {@link #addInsertProfileOperations}. |
| */ |
| private void checkStoredProfile() { |
| assertStoredValues(Profile.CONTENT_URI, cv( |
| Contacts.DISPLAY_NAME, "Profile Contact", |
| RawContacts.LAST_TIME_CONTACTED, 0 |
| )); |
| } |
| |
| public void testTransactionCallback_contactBatch() throws Exception { |
| final ArrayList<ContentProviderOperation> ops = Lists.newArrayList(); |
| |
| addInsertContactOperations(ops); |
| |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| |
| // Execute the operations. |
| mResolver.applyBatch(ContactsContract.AUTHORITY, ops); |
| |
| // Check the result |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertNoTransactionsForProfileMode(); |
| |
| checkStoredContact(); |
| } |
| |
| public void testTransactionCallback_profileBatch() throws Exception { |
| final ArrayList<ContentProviderOperation> ops = Lists.newArrayList(); |
| |
| addInsertProfileOperations(ops); |
| |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| |
| // Execute the operations. |
| mResolver.applyBatch(ContactsContract.AUTHORITY, ops); |
| |
| // Check the result |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertCommitTransactionCalledForProfileMode(); |
| |
| checkStoredProfile(); |
| } |
| |
| public void testTransactionCallback_mixedBatch() throws Exception { |
| final ArrayList<ContentProviderOperation> ops = Lists.newArrayList(); |
| |
| // Create a raw contact and a profile raw contact in a single batch. |
| |
| addInsertContactOperations(ops); |
| addInsertProfileOperations(ops); |
| |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| |
| // Execute the operations. |
| mResolver.applyBatch(ContactsContract.AUTHORITY, ops); |
| |
| // Check the result |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertCommitTransactionCalledForProfileMode(); |
| |
| checkStoredProfile(); |
| checkStoredContact(); |
| } |
| |
| public void testTransactionCallback_mixedBatchReversed() throws Exception { |
| final ArrayList<ContentProviderOperation> ops = Lists.newArrayList(); |
| |
| // Create a profile raw contact and a raw contact in a single batch. |
| |
| addInsertProfileOperations(ops); |
| addInsertContactOperations(ops); |
| |
| mProvider.resetTrasactionCallbackCalledFlags(); |
| |
| // Execute the operations. |
| mResolver.applyBatch(ContactsContract.AUTHORITY, ops); |
| |
| // Check the result |
| mProvider.assertCommitTransactionCalledForContactMode(); |
| mProvider.assertCommitTransactionCalledForProfileMode(); |
| |
| checkStoredProfile(); |
| checkStoredContact(); |
| } |
| } |