blob: 77acb98cd99e42cd0745e05d9a8e56b0fe3f75c2 [file] [log] [blame]
Yorke Lee2644d942013-10-28 11:05:43 -07001/*
2 * Copyright (C) 2009 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.contacts.common;
18
Yorke Lee2644d942013-10-28 11:05:43 -070019import android.content.ContentProviderOperation;
20import android.content.ContentValues;
21import android.content.Context;
22import android.net.Uri;
23import android.provider.BaseColumns;
24import android.provider.ContactsContract.AggregationExceptions;
25import android.provider.ContactsContract.CommonDataKinds.Email;
26import android.provider.ContactsContract.CommonDataKinds.Phone;
27import android.provider.ContactsContract.Data;
28import android.provider.ContactsContract.RawContacts;
29import android.test.AndroidTestCase;
30import android.test.suitebuilder.annotation.LargeTest;
31
32import com.android.contacts.common.RawContactModifierTests.MockContactsSource;
Wenyi Wangf46a6192016-02-18 16:34:37 -080033import com.android.contacts.common.compat.CompatUtils;
34import com.android.contacts.common.model.CPOWrapper;
Yorke Lee2644d942013-10-28 11:05:43 -070035import com.android.contacts.common.model.RawContact;
36import com.android.contacts.common.model.RawContactDelta;
37import com.android.contacts.common.model.ValuesDelta;
38import com.android.contacts.common.model.RawContactDeltaList;
39import com.android.contacts.common.model.RawContactModifier;
40import com.android.contacts.common.model.account.AccountType;
41import com.google.common.collect.Lists;
42
43
44import java.lang.reflect.Field;
45import java.util.ArrayList;
46import java.util.Collections;
47
48/**
49 * Tests for {@link RawContactDeltaList} which focus on "diff" operations that should
50 * create {@link AggregationExceptions} in certain cases.
51 */
52@LargeTest
53public class RawContactDeltaListTests extends AndroidTestCase {
54 public static final String TAG = RawContactDeltaListTests.class.getSimpleName();
55
Jay Shrauner7bae0632015-08-27 16:23:44 -070056 // From android.content.ContentProviderOperation
57 public static final int TYPE_INSERT = 1;
58 public static final int TYPE_UPDATE = 2;
59 public static final int TYPE_DELETE = 3;
60 public static final int TYPE_ASSERT = 4;
61
Yorke Lee2644d942013-10-28 11:05:43 -070062 private static final long CONTACT_FIRST = 1;
63 private static final long CONTACT_SECOND = 2;
64
65 public static final long CONTACT_BOB = 10;
66 public static final long CONTACT_MARY = 11;
67
68 public static final long PHONE_RED = 20;
69 public static final long PHONE_GREEN = 21;
70 public static final long PHONE_BLUE = 22;
71
72 public static final long EMAIL_YELLOW = 25;
73
74 public static final long VER_FIRST = 100;
75 public static final long VER_SECOND = 200;
76
77 public static final String TEST_PHONE = "555-1212";
78 public static final String TEST_ACCOUNT = "org.example.test";
79
80 public RawContactDeltaListTests() {
81 super();
82 }
83
84 @Override
85 public void setUp() {
86 mContext = getContext();
87 }
88
89 /**
90 * Build a {@link AccountType} that has various odd constraints for
91 * testing purposes.
92 */
93 protected AccountType getAccountType() {
94 return new MockContactsSource();
95 }
96
97 static ContentValues getValues(ContentProviderOperation operation)
98 throws NoSuchFieldException, IllegalAccessException {
99 final Field field = ContentProviderOperation.class.getDeclaredField("mValues");
100 field.setAccessible(true);
101 return (ContentValues) field.get(operation);
102 }
103
104 static RawContactDelta getUpdate(Context context, long rawContactId) {
105 final RawContact before = RawContactDeltaTests.getRawContact(context, rawContactId,
106 RawContactDeltaTests.TEST_PHONE_ID);
107 return RawContactDelta.fromBefore(before);
108 }
109
110 static RawContactDelta getInsert() {
111 final ContentValues after = new ContentValues();
112 after.put(RawContacts.ACCOUNT_NAME, RawContactDeltaTests.TEST_ACCOUNT_NAME);
113 after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
114
115 final ValuesDelta values = ValuesDelta.fromAfter(after);
116 return new RawContactDelta(values);
117 }
118
119 static RawContactDeltaList buildSet(RawContactDelta... deltas) {
120 final RawContactDeltaList set = new RawContactDeltaList();
121 Collections.addAll(set, deltas);
122 return set;
123 }
124
125 static RawContactDelta buildBeforeEntity(Context context, long rawContactId, long version,
126 ContentValues... entries) {
127 // Build an existing contact read from database
128 final ContentValues contact = new ContentValues();
129 contact.put(RawContacts.VERSION, version);
130 contact.put(RawContacts._ID, rawContactId);
131 final RawContact before = new RawContact(contact);
132 for (ContentValues entry : entries) {
133 before.addDataItemValues(entry);
134 }
135 return RawContactDelta.fromBefore(before);
136 }
137
138 static RawContactDelta buildAfterEntity(ContentValues... entries) {
139 // Build an existing contact read from database
140 final ContentValues contact = new ContentValues();
141 contact.put(RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT);
142 final RawContactDelta after = new RawContactDelta(ValuesDelta.fromAfter(contact));
143 for (ContentValues entry : entries) {
144 after.addEntry(ValuesDelta.fromAfter(entry));
145 }
146 return after;
147 }
148
149 static ContentValues buildPhone(long phoneId) {
150 return buildPhone(phoneId, Long.toString(phoneId));
151 }
152
153 static ContentValues buildPhone(long phoneId, String value) {
154 final ContentValues values = new ContentValues();
155 values.put(Data._ID, phoneId);
156 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
157 values.put(Phone.NUMBER, value);
158 values.put(Phone.TYPE, Phone.TYPE_HOME);
159 return values;
160 }
161
162 static ContentValues buildEmail(long emailId) {
163 final ContentValues values = new ContentValues();
164 values.put(Data._ID, emailId);
165 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
166 values.put(Email.DATA, Long.toString(emailId));
167 values.put(Email.TYPE, Email.TYPE_HOME);
168 return values;
169 }
170
171 static void insertPhone(RawContactDeltaList set, long rawContactId, ContentValues values) {
172 final RawContactDelta match = set.getByRawContactId(rawContactId);
173 match.addEntry(ValuesDelta.fromAfter(values));
174 }
175
176 static ValuesDelta getPhone(RawContactDeltaList set, long rawContactId, long dataId) {
177 final RawContactDelta match = set.getByRawContactId(rawContactId);
178 return match.getEntry(dataId);
179 }
180
Wenyi Wang009d63c2016-02-18 16:45:08 -0800181 static void assertDiffPattern(RawContactDelta delta, CPOWrapper... pattern) {
182 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
183 delta.buildAssertWrapper(diff);
184 delta.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700185 assertDiffPattern(diff, pattern);
186 }
187
Wenyi Wang009d63c2016-02-18 16:45:08 -0800188 static void assertDiffPattern(RawContactDeltaList set, CPOWrapper... pattern) {
189 assertDiffPattern(set.buildDiffWrapper(), pattern);
Yorke Lee2644d942013-10-28 11:05:43 -0700190 }
191
Wenyi Wang009d63c2016-02-18 16:45:08 -0800192 static void assertDiffPattern(ArrayList<CPOWrapper> diff, CPOWrapper... pattern) {
Yorke Lee2644d942013-10-28 11:05:43 -0700193 assertEquals("Unexpected operations", pattern.length, diff.size());
194 for (int i = 0; i < pattern.length; i++) {
Wenyi Wang009d63c2016-02-18 16:45:08 -0800195 final CPOWrapper expected = pattern[i];
196 final CPOWrapper found = diff.get(i);
Yorke Lee2644d942013-10-28 11:05:43 -0700197
Wenyi Wang009d63c2016-02-18 16:45:08 -0800198 assertEquals("Unexpected uri",
199 expected.getOperation().getUri(), found.getOperation().getUri());
Yorke Lee2644d942013-10-28 11:05:43 -0700200
Jay Shrauner7bae0632015-08-27 16:23:44 -0700201 final String expectedType = getTypeString(expected);
202 final String foundType = getTypeString(found);
Yorke Lee2644d942013-10-28 11:05:43 -0700203 assertEquals("Unexpected type", expectedType, foundType);
204
Wenyi Wang009d63c2016-02-18 16:45:08 -0800205 if (CompatUtils.isDeleteCompat(expected)) continue;
Yorke Lee2644d942013-10-28 11:05:43 -0700206
207 try {
Wenyi Wang009d63c2016-02-18 16:45:08 -0800208 final ContentValues expectedValues = getValues(expected.getOperation());
209 final ContentValues foundValues = getValues(found.getOperation());
Yorke Lee2644d942013-10-28 11:05:43 -0700210
211 expectedValues.remove(BaseColumns._ID);
212 foundValues.remove(BaseColumns._ID);
213
214 assertEquals("Unexpected values", expectedValues, foundValues);
215 } catch (NoSuchFieldException e) {
216 fail(e.toString());
217 } catch (IllegalAccessException e) {
218 fail(e.toString());
219 }
220 }
221 }
222
Wenyi Wang009d63c2016-02-18 16:45:08 -0800223 static String getTypeString(CPOWrapper cpoWrapper) {
224 if (CompatUtils.isAssertQueryCompat(cpoWrapper)) {
Jay Shrauner7bae0632015-08-27 16:23:44 -0700225 return "TYPE_ASSERT";
Wenyi Wang009d63c2016-02-18 16:45:08 -0800226 } else if (CompatUtils.isInsertCompat(cpoWrapper)) {
Jay Shrauner7bae0632015-08-27 16:23:44 -0700227 return "TYPE_INSERT";
Wenyi Wang009d63c2016-02-18 16:45:08 -0800228 } else if (CompatUtils.isUpdateCompat(cpoWrapper)) {
Jay Shrauner7bae0632015-08-27 16:23:44 -0700229 return "TYPE_UPDATE";
Wenyi Wang009d63c2016-02-18 16:45:08 -0800230 } else if (CompatUtils.isDeleteCompat(cpoWrapper)) {
Jay Shrauner7bae0632015-08-27 16:23:44 -0700231 return "TYPE_DELETE";
Yorke Lee2644d942013-10-28 11:05:43 -0700232 }
Jay Shrauner7bae0632015-08-27 16:23:44 -0700233 return "TYPE_UNKNOWN";
Yorke Lee2644d942013-10-28 11:05:43 -0700234 }
235
Wenyi Wang009d63c2016-02-18 16:45:08 -0800236 static CPOWrapper buildAssertVersion(long version) {
Yorke Lee2644d942013-10-28 11:05:43 -0700237 final ContentValues values = new ContentValues();
238 values.put(RawContacts.VERSION, version);
Wenyi Wang009d63c2016-02-18 16:45:08 -0800239 return buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_ASSERT, values);
Yorke Lee2644d942013-10-28 11:05:43 -0700240 }
241
Wenyi Wang009d63c2016-02-18 16:45:08 -0800242 static CPOWrapper buildAggregationModeUpdate(int mode) {
Yorke Lee2644d942013-10-28 11:05:43 -0700243 final ContentValues values = new ContentValues();
244 values.put(RawContacts.AGGREGATION_MODE, mode);
Wenyi Wang009d63c2016-02-18 16:45:08 -0800245 return buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_UPDATE, values);
Yorke Lee2644d942013-10-28 11:05:43 -0700246 }
247
Wenyi Wang009d63c2016-02-18 16:45:08 -0800248 static CPOWrapper buildUpdateAggregationSuspended() {
Yorke Lee2644d942013-10-28 11:05:43 -0700249 return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_SUSPENDED);
250 }
251
Wenyi Wang009d63c2016-02-18 16:45:08 -0800252 static CPOWrapper buildUpdateAggregationDefault() {
Yorke Lee2644d942013-10-28 11:05:43 -0700253 return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT);
254 }
255
Wenyi Wang009d63c2016-02-18 16:45:08 -0800256 static CPOWrapper buildUpdateAggregationKeepTogether(long rawContactId) {
Yorke Lee2644d942013-10-28 11:05:43 -0700257 final ContentValues values = new ContentValues();
258 values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
259 values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
Wenyi Wang009d63c2016-02-18 16:45:08 -0800260 return buildCPOWrapper(AggregationExceptions.CONTENT_URI, TYPE_UPDATE, values);
Yorke Lee2644d942013-10-28 11:05:43 -0700261 }
262
263 static ContentValues buildDataInsert(ValuesDelta values, long rawContactId) {
264 final ContentValues insertValues = values.getCompleteValues();
265 insertValues.put(Data.RAW_CONTACT_ID, rawContactId);
266 return insertValues;
267 }
268
Wenyi Wang009d63c2016-02-18 16:45:08 -0800269 static CPOWrapper buildDelete(Uri uri) {
270 return buildCPOWrapper(uri, TYPE_DELETE, (ContentValues) null);
Yorke Lee2644d942013-10-28 11:05:43 -0700271 }
272
273 static ContentProviderOperation buildOper(Uri uri, int type, ValuesDelta values) {
274 return buildOper(uri, type, values.getCompleteValues());
275 }
276
277 static ContentProviderOperation buildOper(Uri uri, int type, ContentValues values) {
278 switch (type) {
279 case TYPE_ASSERT:
280 return ContentProviderOperation.newAssertQuery(uri).withValues(values).build();
281 case TYPE_INSERT:
282 return ContentProviderOperation.newInsert(uri).withValues(values).build();
283 case TYPE_UPDATE:
284 return ContentProviderOperation.newUpdate(uri).withValues(values).build();
285 case TYPE_DELETE:
286 return ContentProviderOperation.newDelete(uri).build();
287 }
288 return null;
289 }
290
Wenyi Wang009d63c2016-02-18 16:45:08 -0800291 static CPOWrapper buildCPOWrapper(Uri uri, int type, ContentValues values) {
292 if (type == TYPE_ASSERT || type == TYPE_INSERT || type == TYPE_UPDATE
293 || type == TYPE_DELETE) {
294 return new CPOWrapper(buildOper(uri, type, values), type);
295 }
296 return null;
297 }
298
Yorke Lee2644d942013-10-28 11:05:43 -0700299 static Long getVersion(RawContactDeltaList set, Long rawContactId) {
300 return set.getByRawContactId(rawContactId).getValues().getAsLong(RawContacts.VERSION);
301 }
302
303 /**
304 * Count number of {@link AggregationExceptions} updates contained in the
Wenyi Wangf46a6192016-02-18 16:34:37 -0800305 * given list of {@link CPOWrapper}.
Yorke Lee2644d942013-10-28 11:05:43 -0700306 */
Wenyi Wangf46a6192016-02-18 16:34:37 -0800307 static int countExceptionUpdates(ArrayList<CPOWrapper> diff) {
Yorke Lee2644d942013-10-28 11:05:43 -0700308 int updateCount = 0;
Wenyi Wangf46a6192016-02-18 16:34:37 -0800309 for (CPOWrapper cpoWrapper : diff) {
310 final ContentProviderOperation oper = cpoWrapper.getOperation();
Yorke Lee2644d942013-10-28 11:05:43 -0700311 if (AggregationExceptions.CONTENT_URI.equals(oper.getUri())
Wenyi Wangf46a6192016-02-18 16:34:37 -0800312 && CompatUtils.isUpdateCompat(cpoWrapper)) {
Yorke Lee2644d942013-10-28 11:05:43 -0700313 updateCount++;
314 }
315 }
316 return updateCount;
317 }
318
319 public void testInsert() {
320 final RawContactDelta insert = getInsert();
321 final RawContactDeltaList set = buildSet(insert);
322
323 // Inserting single shouldn't create rules
Wenyi Wangf46a6192016-02-18 16:34:37 -0800324 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700325 final int exceptionCount = countExceptionUpdates(diff);
326 assertEquals("Unexpected exception updates", 0, exceptionCount);
327 }
328
329 public void testUpdateUpdate() {
330 final RawContactDelta updateFirst = getUpdate(mContext, CONTACT_FIRST);
331 final RawContactDelta updateSecond = getUpdate(mContext, CONTACT_SECOND);
332 final RawContactDeltaList set = buildSet(updateFirst, updateSecond);
333
334 // Updating two existing shouldn't create rules
Wenyi Wangf46a6192016-02-18 16:34:37 -0800335 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700336 final int exceptionCount = countExceptionUpdates(diff);
337 assertEquals("Unexpected exception updates", 0, exceptionCount);
338 }
339
340 public void testUpdateInsert() {
341 final RawContactDelta update = getUpdate(mContext, CONTACT_FIRST);
342 final RawContactDelta insert = getInsert();
343 final RawContactDeltaList set = buildSet(update, insert);
344
345 // New insert should only create one rule
Wenyi Wangf46a6192016-02-18 16:34:37 -0800346 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700347 final int exceptionCount = countExceptionUpdates(diff);
348 assertEquals("Unexpected exception updates", 1, exceptionCount);
349 }
350
351 public void testInsertUpdateInsert() {
352 final RawContactDelta insertFirst = getInsert();
353 final RawContactDelta update = getUpdate(mContext, CONTACT_FIRST);
354 final RawContactDelta insertSecond = getInsert();
355 final RawContactDeltaList set = buildSet(insertFirst, update, insertSecond);
356
357 // Two inserts should create two rules to bind against single existing
Wenyi Wangf46a6192016-02-18 16:34:37 -0800358 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700359 final int exceptionCount = countExceptionUpdates(diff);
360 assertEquals("Unexpected exception updates", 2, exceptionCount);
361 }
362
363 public void testInsertInsertInsert() {
364 final RawContactDelta insertFirst = getInsert();
365 final RawContactDelta insertSecond = getInsert();
366 final RawContactDelta insertThird = getInsert();
367 final RawContactDeltaList set = buildSet(insertFirst, insertSecond, insertThird);
368
369 // Three new inserts should create only two binding rules
Wenyi Wangf46a6192016-02-18 16:34:37 -0800370 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700371 final int exceptionCount = countExceptionUpdates(diff);
372 assertEquals("Unexpected exception updates", 2, exceptionCount);
373 }
374
375 public void testMergeDataRemoteInsert() {
376 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
377 VER_FIRST, buildPhone(PHONE_RED)));
378 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
379 VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
380
381 // Merge in second version, verify they match
382 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
383 assertEquals("Unexpected change when merging", second, merged);
384 }
385
386 public void testMergeDataLocalUpdateRemoteInsert() {
387 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
388 VER_FIRST, buildPhone(PHONE_RED)));
389 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
390 VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
391
392 // Change the local number to trigger update
393 final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
394 phone.put(Phone.NUMBER, TEST_PHONE);
395
396 assertDiffPattern(first,
397 buildAssertVersion(VER_FIRST),
398 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800399 buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
Yorke Lee2644d942013-10-28 11:05:43 -0700400 buildUpdateAggregationDefault());
401
402 // Merge in the second version, verify diff matches
403 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
404 assertDiffPattern(merged,
405 buildAssertVersion(VER_SECOND),
406 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800407 buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
Yorke Lee2644d942013-10-28 11:05:43 -0700408 buildUpdateAggregationDefault());
409 }
410
411 public void testMergeDataLocalUpdateRemoteDelete() {
412 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
413 VER_FIRST, buildPhone(PHONE_RED)));
414 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
415 VER_SECOND, buildPhone(PHONE_GREEN)));
416
417 // Change the local number to trigger update
418 final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
419 phone.put(Phone.NUMBER, TEST_PHONE);
420
421 assertDiffPattern(first,
422 buildAssertVersion(VER_FIRST),
423 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800424 buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
Yorke Lee2644d942013-10-28 11:05:43 -0700425 buildUpdateAggregationDefault());
426
427 // Merge in the second version, verify that our update changed to
428 // insert, since RED was deleted on remote side
429 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
430 assertDiffPattern(merged,
431 buildAssertVersion(VER_SECOND),
432 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800433 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(phone, CONTACT_BOB)),
Yorke Lee2644d942013-10-28 11:05:43 -0700434 buildUpdateAggregationDefault());
435 }
436
437 public void testMergeDataLocalDeleteRemoteUpdate() {
438 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
439 VER_FIRST, buildPhone(PHONE_RED)));
440 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
441 VER_SECOND, buildPhone(PHONE_RED, TEST_PHONE)));
442
443 // Delete phone locally
444 final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
445 phone.markDeleted();
446
447 assertDiffPattern(first,
448 buildAssertVersion(VER_FIRST),
449 buildUpdateAggregationSuspended(),
450 buildDelete(Data.CONTENT_URI),
451 buildUpdateAggregationDefault());
452
453 // Merge in the second version, verify that our delete remains
454 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
455 assertDiffPattern(merged,
456 buildAssertVersion(VER_SECOND),
457 buildUpdateAggregationSuspended(),
458 buildDelete(Data.CONTENT_URI),
459 buildUpdateAggregationDefault());
460 }
461
462 public void testMergeDataLocalInsertRemoteInsert() {
463 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
464 VER_FIRST, buildPhone(PHONE_RED)));
465 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
466 VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
467
468 // Insert new phone locally
469 final ValuesDelta bluePhone = ValuesDelta.fromAfter(buildPhone(PHONE_BLUE));
470 first.getByRawContactId(CONTACT_BOB).addEntry(bluePhone);
471 assertDiffPattern(first,
472 buildAssertVersion(VER_FIRST),
473 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800474 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)),
Yorke Lee2644d942013-10-28 11:05:43 -0700475 buildUpdateAggregationDefault());
476
477 // Merge in the second version, verify that our insert remains
478 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
479 assertDiffPattern(merged,
480 buildAssertVersion(VER_SECOND),
481 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800482 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)),
Yorke Lee2644d942013-10-28 11:05:43 -0700483 buildUpdateAggregationDefault());
484 }
485
486 public void testMergeRawContactLocalInsertRemoteInsert() {
487 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
488 VER_FIRST, buildPhone(PHONE_RED)));
489 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
490 VER_SECOND, buildPhone(PHONE_RED)), buildBeforeEntity(mContext, CONTACT_MARY,
491 VER_SECOND, buildPhone(PHONE_RED)));
492
493 // Add new contact locally, should remain insert
494 final ContentValues joePhoneInsert = buildPhone(PHONE_BLUE);
495 final RawContactDelta joeContact = buildAfterEntity(joePhoneInsert);
496 final ContentValues joeContactInsert = joeContact.getValues().getCompleteValues();
497 joeContactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
498 first.add(joeContact);
499 assertDiffPattern(first,
500 buildAssertVersion(VER_FIRST),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800501 buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert),
502 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert),
Yorke Lee2644d942013-10-28 11:05:43 -0700503 buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
504 buildUpdateAggregationKeepTogether(CONTACT_BOB));
505
506 // Merge in the second version, verify that our insert remains
507 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
508 assertDiffPattern(merged,
509 buildAssertVersion(VER_SECOND),
510 buildAssertVersion(VER_SECOND),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800511 buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert),
512 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert),
Yorke Lee2644d942013-10-28 11:05:43 -0700513 buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
514 buildUpdateAggregationKeepTogether(CONTACT_BOB));
515 }
516
517 public void testMergeRawContactLocalDeleteRemoteDelete() {
518 final RawContactDeltaList first = buildSet(
519 buildBeforeEntity(mContext, CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
520 buildBeforeEntity(mContext, CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
521 final RawContactDeltaList second = buildSet(
522 buildBeforeEntity(mContext, CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
523
524 // Remove contact locally
525 first.getByRawContactId(CONTACT_MARY).markDeleted();
526 assertDiffPattern(first,
527 buildAssertVersion(VER_FIRST),
528 buildAssertVersion(VER_FIRST),
529 buildDelete(RawContacts.CONTENT_URI));
530
531 // Merge in the second version, verify that our delete isn't needed
532 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
533 assertDiffPattern(merged);
534 }
535
536 public void testMergeRawContactLocalUpdateRemoteDelete() {
537 final RawContactDeltaList first = buildSet(
538 buildBeforeEntity(mContext, CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
539 buildBeforeEntity(mContext, CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
540 final RawContactDeltaList second = buildSet(
541 buildBeforeEntity(mContext, CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
542
543 // Perform local update
544 final ValuesDelta phone = getPhone(first, CONTACT_MARY, PHONE_RED);
545 phone.put(Phone.NUMBER, TEST_PHONE);
546 assertDiffPattern(first,
547 buildAssertVersion(VER_FIRST),
548 buildAssertVersion(VER_FIRST),
549 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800550 buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
Yorke Lee2644d942013-10-28 11:05:43 -0700551 buildUpdateAggregationDefault());
552
553 final ContentValues phoneInsert = phone.getCompleteValues();
554 final ContentValues contactInsert = first.getByRawContactId(CONTACT_MARY).getValues()
555 .getCompleteValues();
556 contactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
557
558 // Merge and verify that update turned into insert
559 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
560 assertDiffPattern(merged,
561 buildAssertVersion(VER_SECOND),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800562 buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, contactInsert),
563 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, phoneInsert),
Yorke Lee2644d942013-10-28 11:05:43 -0700564 buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
565 buildUpdateAggregationKeepTogether(CONTACT_BOB));
566 }
567
568 public void testMergeUsesNewVersion() {
569 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
570 VER_FIRST, buildPhone(PHONE_RED)));
571 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
572 VER_SECOND, buildPhone(PHONE_RED)));
573
574 assertEquals((Long)VER_FIRST, getVersion(first, CONTACT_BOB));
575 assertEquals((Long)VER_SECOND, getVersion(second, CONTACT_BOB));
576
577 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
578 assertEquals((Long)VER_SECOND, getVersion(merged, CONTACT_BOB));
579 }
580
581 public void testMergeAfterEnsureAndTrim() {
582 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
583 VER_FIRST, buildEmail(EMAIL_YELLOW)));
584 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
585 VER_SECOND, buildEmail(EMAIL_YELLOW)));
586
587 // Ensure we have at least one phone
588 final AccountType source = getAccountType();
589 final RawContactDelta bobContact = first.getByRawContactId(CONTACT_BOB);
590 RawContactModifier.ensureKindExists(bobContact, source, Phone.CONTENT_ITEM_TYPE);
591 final ValuesDelta bobPhone = bobContact.getSuperPrimaryEntry(Phone.CONTENT_ITEM_TYPE, true);
592
593 // Make sure the update would insert a row
594 assertDiffPattern(first,
595 buildAssertVersion(VER_FIRST),
596 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800597 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bobPhone, CONTACT_BOB)),
Yorke Lee2644d942013-10-28 11:05:43 -0700598 buildUpdateAggregationDefault());
599
600 // Trim values and ensure that we don't insert things
601 RawContactModifier.trimEmpty(bobContact, source);
602 assertDiffPattern(first);
603
604 // Now re-parent the change, which should remain no-op
605 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
606 assertDiffPattern(merged);
607 }
608}