| /* |
| * Copyright (C) 2009 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.contacts.model; |
| |
| import android.content.ContentProviderOperation; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.net.Uri; |
| import android.provider.BaseColumns; |
| import android.provider.ContactsContract.AggregationExceptions; |
| import android.provider.ContactsContract.CommonDataKinds.Email; |
| import android.provider.ContactsContract.CommonDataKinds.Phone; |
| import android.provider.ContactsContract.Data; |
| import android.provider.ContactsContract.RawContacts; |
| import android.test.AndroidTestCase; |
| import android.test.suitebuilder.annotation.LargeTest; |
| |
| import com.android.contacts.compat.CompatUtils; |
| import com.android.contacts.model.account.AccountType; |
| |
| import com.google.common.collect.Lists; |
| |
| import java.lang.reflect.Field; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| |
| /** |
| * Tests for {@link RawContactDeltaList} which focus on "diff" operations that should |
| * create {@link AggregationExceptions} in certain cases. |
| */ |
| @LargeTest |
| public class RawContactDeltaListTests extends AndroidTestCase { |
| public static final String TAG = RawContactDeltaListTests.class.getSimpleName(); |
| |
| // From android.content.ContentProviderOperation |
| public static final int TYPE_INSERT = 1; |
| public static final int TYPE_UPDATE = 2; |
| public static final int TYPE_DELETE = 3; |
| public static final int TYPE_ASSERT = 4; |
| |
| private static final long CONTACT_FIRST = 1; |
| private static final long CONTACT_SECOND = 2; |
| |
| public static final long CONTACT_BOB = 10; |
| public static final long CONTACT_MARY = 11; |
| |
| public static final long PHONE_RED = 20; |
| public static final long PHONE_GREEN = 21; |
| public static final long PHONE_BLUE = 22; |
| |
| public static final long EMAIL_YELLOW = 25; |
| |
| public static final long VER_FIRST = 100; |
| public static final long VER_SECOND = 200; |
| |
| public static final String TEST_PHONE = "555-1212"; |
| public static final String TEST_ACCOUNT = "org.example.test"; |
| |
| public RawContactDeltaListTests() { |
| super(); |
| } |
| |
| @Override |
| public void setUp() { |
| mContext = getContext(); |
| } |
| |
| /** |
| * Build a {@link AccountType} that has various odd constraints for |
| * testing purposes. |
| */ |
| protected AccountType getAccountType() { |
| return new RawContactModifierTests.MockContactsSource(); |
| } |
| |
| static ContentValues getValues(ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = ContentProviderOperation.class.getDeclaredField("mValues"); |
| field.setAccessible(true); |
| return (ContentValues) field.get(operation); |
| } |
| |
| static RawContactDelta getUpdate(Context context, long rawContactId) { |
| final RawContact before = RawContactDeltaTests.getRawContact(context, rawContactId, |
| RawContactDeltaTests.TEST_PHONE_ID); |
| return RawContactDelta.fromBefore(before); |
| } |
| |
| static RawContactDelta getInsert() { |
| final ContentValues after = new ContentValues(); |
| after.put(RawContacts.ACCOUNT_NAME, RawContactDeltaTests.TEST_ACCOUNT_NAME); |
| after.put(RawContacts.SEND_TO_VOICEMAIL, 1); |
| |
| final ValuesDelta values = ValuesDelta.fromAfter(after); |
| return new RawContactDelta(values); |
| } |
| |
| static RawContactDeltaList buildSet(RawContactDelta... deltas) { |
| final RawContactDeltaList set = new RawContactDeltaList(); |
| Collections.addAll(set, deltas); |
| return set; |
| } |
| |
| static RawContactDelta buildBeforeEntity(Context context, long rawContactId, long version, |
| ContentValues... entries) { |
| // Build an existing contact read from database |
| final ContentValues contact = new ContentValues(); |
| contact.put(RawContacts.VERSION, version); |
| contact.put(RawContacts._ID, rawContactId); |
| final RawContact before = new RawContact(contact); |
| for (ContentValues entry : entries) { |
| before.addDataItemValues(entry); |
| } |
| return RawContactDelta.fromBefore(before); |
| } |
| |
| static RawContactDelta buildAfterEntity(ContentValues... entries) { |
| // Build an existing contact read from database |
| final ContentValues contact = new ContentValues(); |
| contact.put(RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT); |
| final RawContactDelta after = new RawContactDelta(ValuesDelta.fromAfter(contact)); |
| for (ContentValues entry : entries) { |
| after.addEntry(ValuesDelta.fromAfter(entry)); |
| } |
| return after; |
| } |
| |
| static ContentValues buildPhone(long phoneId) { |
| return buildPhone(phoneId, Long.toString(phoneId)); |
| } |
| |
| static ContentValues buildPhone(long phoneId, String value) { |
| final ContentValues values = new ContentValues(); |
| values.put(Data._ID, phoneId); |
| values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); |
| values.put(Phone.NUMBER, value); |
| values.put(Phone.TYPE, Phone.TYPE_HOME); |
| return values; |
| } |
| |
| static ContentValues buildEmail(long emailId) { |
| final ContentValues values = new ContentValues(); |
| values.put(Data._ID, emailId); |
| values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); |
| values.put(Email.DATA, Long.toString(emailId)); |
| values.put(Email.TYPE, Email.TYPE_HOME); |
| return values; |
| } |
| |
| static void insertPhone(RawContactDeltaList set, long rawContactId, ContentValues values) { |
| final RawContactDelta match = set.getByRawContactId(rawContactId); |
| match.addEntry(ValuesDelta.fromAfter(values)); |
| } |
| |
| static ValuesDelta getPhone(RawContactDeltaList set, long rawContactId, long dataId) { |
| final RawContactDelta match = set.getByRawContactId(rawContactId); |
| return match.getEntry(dataId); |
| } |
| |
| static void assertDiffPattern(RawContactDelta delta, CPOWrapper... pattern) { |
| final ArrayList<CPOWrapper> diff = Lists.newArrayList(); |
| delta.buildAssertWrapper(diff); |
| delta.buildDiffWrapper(diff); |
| assertDiffPattern(diff, pattern); |
| } |
| |
| static void assertDiffPattern(RawContactDeltaList set, CPOWrapper... pattern) { |
| assertDiffPattern(set.buildDiffWrapper(), pattern); |
| } |
| |
| static void assertDiffPattern(ArrayList<CPOWrapper> diff, CPOWrapper... pattern) { |
| assertEquals("Unexpected operations", pattern.length, diff.size()); |
| for (int i = 0; i < pattern.length; i++) { |
| final CPOWrapper expected = pattern[i]; |
| final CPOWrapper found = diff.get(i); |
| |
| assertEquals("Unexpected uri", |
| expected.getOperation().getUri(), found.getOperation().getUri()); |
| |
| final String expectedType = getTypeString(expected); |
| final String foundType = getTypeString(found); |
| assertEquals("Unexpected type", expectedType, foundType); |
| |
| if (CompatUtils.isDeleteCompat(expected)) continue; |
| |
| try { |
| final ContentValues expectedValues = getValues(expected.getOperation()); |
| final ContentValues foundValues = getValues(found.getOperation()); |
| |
| expectedValues.remove(BaseColumns._ID); |
| foundValues.remove(BaseColumns._ID); |
| |
| assertEquals("Unexpected values", expectedValues, foundValues); |
| } catch (NoSuchFieldException e) { |
| fail(e.toString()); |
| } catch (IllegalAccessException e) { |
| fail(e.toString()); |
| } |
| } |
| } |
| |
| static String getTypeString(CPOWrapper cpoWrapper) { |
| if (CompatUtils.isAssertQueryCompat(cpoWrapper)) { |
| return "TYPE_ASSERT"; |
| } else if (CompatUtils.isInsertCompat(cpoWrapper)) { |
| return "TYPE_INSERT"; |
| } else if (CompatUtils.isUpdateCompat(cpoWrapper)) { |
| return "TYPE_UPDATE"; |
| } else if (CompatUtils.isDeleteCompat(cpoWrapper)) { |
| return "TYPE_DELETE"; |
| } |
| return "TYPE_UNKNOWN"; |
| } |
| |
| static CPOWrapper buildAssertVersion(long version) { |
| final ContentValues values = new ContentValues(); |
| values.put(RawContacts.VERSION, version); |
| return buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_ASSERT, values); |
| } |
| |
| static CPOWrapper buildAggregationModeUpdate(int mode) { |
| final ContentValues values = new ContentValues(); |
| values.put(RawContacts.AGGREGATION_MODE, mode); |
| return buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_UPDATE, values); |
| } |
| |
| static CPOWrapper buildUpdateAggregationSuspended() { |
| return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_SUSPENDED); |
| } |
| |
| static CPOWrapper buildUpdateAggregationDefault() { |
| return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT); |
| } |
| |
| static CPOWrapper buildUpdateAggregationKeepTogether(long rawContactId) { |
| final ContentValues values = new ContentValues(); |
| values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId); |
| values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER); |
| return buildCPOWrapper(AggregationExceptions.CONTENT_URI, TYPE_UPDATE, values); |
| } |
| |
| static ContentValues buildDataInsert(ValuesDelta values, long rawContactId) { |
| final ContentValues insertValues = values.getCompleteValues(); |
| insertValues.put(Data.RAW_CONTACT_ID, rawContactId); |
| return insertValues; |
| } |
| |
| static CPOWrapper buildDelete(Uri uri) { |
| return buildCPOWrapper(uri, TYPE_DELETE, (ContentValues) null); |
| } |
| |
| static ContentProviderOperation buildOper(Uri uri, int type, ValuesDelta values) { |
| return buildOper(uri, type, values.getCompleteValues()); |
| } |
| |
| static ContentProviderOperation buildOper(Uri uri, int type, ContentValues values) { |
| switch (type) { |
| case TYPE_ASSERT: |
| return ContentProviderOperation.newAssertQuery(uri).withValues(values).build(); |
| case TYPE_INSERT: |
| return ContentProviderOperation.newInsert(uri).withValues(values).build(); |
| case TYPE_UPDATE: |
| return ContentProviderOperation.newUpdate(uri).withValues(values).build(); |
| case TYPE_DELETE: |
| return ContentProviderOperation.newDelete(uri).build(); |
| } |
| return null; |
| } |
| |
| static CPOWrapper buildCPOWrapper(Uri uri, int type, ContentValues values) { |
| if (type == TYPE_ASSERT || type == TYPE_INSERT || type == TYPE_UPDATE |
| || type == TYPE_DELETE) { |
| return new CPOWrapper(buildOper(uri, type, values), type); |
| } |
| return null; |
| } |
| |
| static Long getVersion(RawContactDeltaList set, Long rawContactId) { |
| return set.getByRawContactId(rawContactId).getValues().getAsLong(RawContacts.VERSION); |
| } |
| |
| /** |
| * Count number of {@link AggregationExceptions} updates contained in the |
| * given list of {@link CPOWrapper}. |
| */ |
| static int countExceptionUpdates(ArrayList<CPOWrapper> diff) { |
| int updateCount = 0; |
| for (CPOWrapper cpoWrapper : diff) { |
| final ContentProviderOperation oper = cpoWrapper.getOperation(); |
| if (AggregationExceptions.CONTENT_URI.equals(oper.getUri()) |
| && CompatUtils.isUpdateCompat(cpoWrapper)) { |
| updateCount++; |
| } |
| } |
| return updateCount; |
| } |
| |
| public void testInsert() { |
| final RawContactDelta insert = getInsert(); |
| final RawContactDeltaList set = buildSet(insert); |
| |
| // Inserting single shouldn't create rules |
| final ArrayList<CPOWrapper> diff = set.buildDiffWrapper(); |
| final int exceptionCount = countExceptionUpdates(diff); |
| assertEquals("Unexpected exception updates", 0, exceptionCount); |
| } |
| |
| public void testUpdateUpdate() { |
| final RawContactDelta updateFirst = getUpdate(mContext, CONTACT_FIRST); |
| final RawContactDelta updateSecond = getUpdate(mContext, CONTACT_SECOND); |
| final RawContactDeltaList set = buildSet(updateFirst, updateSecond); |
| |
| // Updating two existing shouldn't create rules |
| final ArrayList<CPOWrapper> diff = set.buildDiffWrapper(); |
| final int exceptionCount = countExceptionUpdates(diff); |
| assertEquals("Unexpected exception updates", 0, exceptionCount); |
| } |
| |
| public void testUpdateInsert() { |
| final RawContactDelta update = getUpdate(mContext, CONTACT_FIRST); |
| final RawContactDelta insert = getInsert(); |
| final RawContactDeltaList set = buildSet(update, insert); |
| |
| // New insert should only create one rule |
| final ArrayList<CPOWrapper> diff = set.buildDiffWrapper(); |
| final int exceptionCount = countExceptionUpdates(diff); |
| assertEquals("Unexpected exception updates", 1, exceptionCount); |
| } |
| |
| public void testInsertUpdateInsert() { |
| final RawContactDelta insertFirst = getInsert(); |
| final RawContactDelta update = getUpdate(mContext, CONTACT_FIRST); |
| final RawContactDelta insertSecond = getInsert(); |
| final RawContactDeltaList set = buildSet(insertFirst, update, insertSecond); |
| |
| // Two inserts should create two rules to bind against single existing |
| final ArrayList<CPOWrapper> diff = set.buildDiffWrapper(); |
| final int exceptionCount = countExceptionUpdates(diff); |
| assertEquals("Unexpected exception updates", 2, exceptionCount); |
| } |
| |
| public void testInsertInsertInsert() { |
| final RawContactDelta insertFirst = getInsert(); |
| final RawContactDelta insertSecond = getInsert(); |
| final RawContactDelta insertThird = getInsert(); |
| final RawContactDeltaList set = buildSet(insertFirst, insertSecond, insertThird); |
| |
| // Three new inserts should create only two binding rules |
| final ArrayList<CPOWrapper> diff = set.buildDiffWrapper(); |
| final int exceptionCount = countExceptionUpdates(diff); |
| assertEquals("Unexpected exception updates", 2, exceptionCount); |
| } |
| |
| public void testMergeDataRemoteInsert() { |
| final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN))); |
| |
| // Merge in second version, verify they match |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertEquals("Unexpected change when merging", second, merged); |
| } |
| |
| public void testMergeDataLocalUpdateRemoteInsert() { |
| final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN))); |
| |
| // Change the local number to trigger update |
| final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED); |
| phone.put(Phone.NUMBER, TEST_PHONE); |
| |
| assertDiffPattern(first, |
| buildAssertVersion(VER_FIRST), |
| buildUpdateAggregationSuspended(), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()), |
| buildUpdateAggregationDefault()); |
| |
| // Merge in the second version, verify diff matches |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertDiffPattern(merged, |
| buildAssertVersion(VER_SECOND), |
| buildUpdateAggregationSuspended(), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()), |
| buildUpdateAggregationDefault()); |
| } |
| |
| public void testMergeDataLocalUpdateRemoteDelete() { |
| final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_SECOND, buildPhone(PHONE_GREEN))); |
| |
| // Change the local number to trigger update |
| final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED); |
| phone.put(Phone.NUMBER, TEST_PHONE); |
| |
| assertDiffPattern(first, |
| buildAssertVersion(VER_FIRST), |
| buildUpdateAggregationSuspended(), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()), |
| buildUpdateAggregationDefault()); |
| |
| // Merge in the second version, verify that our update changed to |
| // insert, since RED was deleted on remote side |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertDiffPattern(merged, |
| buildAssertVersion(VER_SECOND), |
| buildUpdateAggregationSuspended(), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(phone, CONTACT_BOB)), |
| buildUpdateAggregationDefault()); |
| } |
| |
| public void testMergeDataLocalDeleteRemoteUpdate() { |
| final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_SECOND, buildPhone(PHONE_RED, TEST_PHONE))); |
| |
| // Delete phone locally |
| final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED); |
| phone.markDeleted(); |
| |
| assertDiffPattern(first, |
| buildAssertVersion(VER_FIRST), |
| buildUpdateAggregationSuspended(), |
| buildDelete(Data.CONTENT_URI), |
| buildUpdateAggregationDefault()); |
| |
| // Merge in the second version, verify that our delete remains |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertDiffPattern(merged, |
| buildAssertVersion(VER_SECOND), |
| buildUpdateAggregationSuspended(), |
| buildDelete(Data.CONTENT_URI), |
| buildUpdateAggregationDefault()); |
| } |
| |
| public void testMergeDataLocalInsertRemoteInsert() { |
| final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN))); |
| |
| // Insert new phone locally |
| final ValuesDelta bluePhone = ValuesDelta.fromAfter(buildPhone(PHONE_BLUE)); |
| first.getByRawContactId(CONTACT_BOB).addEntry(bluePhone); |
| assertDiffPattern(first, |
| buildAssertVersion(VER_FIRST), |
| buildUpdateAggregationSuspended(), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)), |
| buildUpdateAggregationDefault()); |
| |
| // Merge in the second version, verify that our insert remains |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertDiffPattern(merged, |
| buildAssertVersion(VER_SECOND), |
| buildUpdateAggregationSuspended(), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)), |
| buildUpdateAggregationDefault()); |
| } |
| |
| public void testMergeRawContactLocalInsertRemoteInsert() { |
| final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_SECOND, buildPhone(PHONE_RED)), buildBeforeEntity(mContext, CONTACT_MARY, |
| VER_SECOND, buildPhone(PHONE_RED))); |
| |
| // Add new contact locally, should remain insert |
| final ContentValues joePhoneInsert = buildPhone(PHONE_BLUE); |
| final RawContactDelta joeContact = buildAfterEntity(joePhoneInsert); |
| final ContentValues joeContactInsert = joeContact.getValues().getCompleteValues(); |
| joeContactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED); |
| first.add(joeContact); |
| assertDiffPattern(first, |
| buildAssertVersion(VER_FIRST), |
| buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert), |
| buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT), |
| buildUpdateAggregationKeepTogether(CONTACT_BOB)); |
| |
| // Merge in the second version, verify that our insert remains |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertDiffPattern(merged, |
| buildAssertVersion(VER_SECOND), |
| buildAssertVersion(VER_SECOND), |
| buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert), |
| buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT), |
| buildUpdateAggregationKeepTogether(CONTACT_BOB)); |
| } |
| |
| public void testMergeRawContactLocalDeleteRemoteDelete() { |
| final RawContactDeltaList first = buildSet( |
| buildBeforeEntity(mContext, CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)), |
| buildBeforeEntity(mContext, CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet( |
| buildBeforeEntity(mContext, CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED))); |
| |
| // Remove contact locally |
| first.getByRawContactId(CONTACT_MARY).markDeleted(); |
| assertDiffPattern(first, |
| buildAssertVersion(VER_FIRST), |
| buildAssertVersion(VER_FIRST), |
| buildDelete(RawContacts.CONTENT_URI)); |
| |
| // Merge in the second version, verify that our delete isn't needed |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertDiffPattern(merged); |
| } |
| |
| public void testMergeRawContactLocalUpdateRemoteDelete() { |
| final RawContactDeltaList first = buildSet( |
| buildBeforeEntity(mContext, CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)), |
| buildBeforeEntity(mContext, CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet( |
| buildBeforeEntity(mContext, CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED))); |
| |
| // Perform local update |
| final ValuesDelta phone = getPhone(first, CONTACT_MARY, PHONE_RED); |
| phone.put(Phone.NUMBER, TEST_PHONE); |
| assertDiffPattern(first, |
| buildAssertVersion(VER_FIRST), |
| buildAssertVersion(VER_FIRST), |
| buildUpdateAggregationSuspended(), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()), |
| buildUpdateAggregationDefault()); |
| |
| final ContentValues phoneInsert = phone.getCompleteValues(); |
| final ContentValues contactInsert = first.getByRawContactId(CONTACT_MARY).getValues() |
| .getCompleteValues(); |
| contactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED); |
| |
| // Merge and verify that update turned into insert |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertDiffPattern(merged, |
| buildAssertVersion(VER_SECOND), |
| buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, contactInsert), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, phoneInsert), |
| buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT), |
| buildUpdateAggregationKeepTogether(CONTACT_BOB)); |
| } |
| |
| public void testMergeUsesNewVersion() { |
| final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_FIRST, buildPhone(PHONE_RED))); |
| final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_SECOND, buildPhone(PHONE_RED))); |
| |
| assertEquals((Long)VER_FIRST, getVersion(first, CONTACT_BOB)); |
| assertEquals((Long)VER_SECOND, getVersion(second, CONTACT_BOB)); |
| |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertEquals((Long)VER_SECOND, getVersion(merged, CONTACT_BOB)); |
| } |
| |
| public void testMergeAfterEnsureAndTrim() { |
| final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_FIRST, buildEmail(EMAIL_YELLOW))); |
| final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB, |
| VER_SECOND, buildEmail(EMAIL_YELLOW))); |
| |
| // Ensure we have at least one phone |
| final AccountType source = getAccountType(); |
| final RawContactDelta bobContact = first.getByRawContactId(CONTACT_BOB); |
| RawContactModifier.ensureKindExists(bobContact, source, Phone.CONTENT_ITEM_TYPE); |
| final ValuesDelta bobPhone = bobContact.getSuperPrimaryEntry(Phone.CONTENT_ITEM_TYPE, true); |
| |
| // Make sure the update would insert a row |
| assertDiffPattern(first, |
| buildAssertVersion(VER_FIRST), |
| buildUpdateAggregationSuspended(), |
| buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bobPhone, CONTACT_BOB)), |
| buildUpdateAggregationDefault()); |
| |
| // Trim values and ensure that we don't insert things |
| RawContactModifier.trimEmpty(bobContact, source); |
| assertDiffPattern(first); |
| |
| // Now re-parent the change, which should remain no-op |
| final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first); |
| assertDiffPattern(merged); |
| } |
| } |