blob: 45654958f31df2f3fb4821fded310cf096dd8ec3 [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
Gary Mai0a49afa2016-12-05 15:53:58 -080017package com.android.contacts.model;
Yorke Lee2644d942013-10-28 11:05:43 -070018
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
Gary Mai69c182a2016-12-05 13:07:03 -080032import com.android.contacts.compat.CompatUtils;
Gary Mai69c182a2016-12-05 13:07:03 -080033import com.android.contacts.model.account.AccountType;
Yorke Lee2644d942013-10-28 11:05:43 -070034
Gary Mai0a49afa2016-12-05 15:53:58 -080035import com.google.common.collect.Lists;
Yorke Lee2644d942013-10-28 11:05:43 -070036
37import java.lang.reflect.Field;
38import java.util.ArrayList;
39import java.util.Collections;
40
41/**
42 * Tests for {@link RawContactDeltaList} which focus on "diff" operations that should
43 * create {@link AggregationExceptions} in certain cases.
44 */
45@LargeTest
46public class RawContactDeltaListTests extends AndroidTestCase {
47 public static final String TAG = RawContactDeltaListTests.class.getSimpleName();
48
Jay Shrauner7bae0632015-08-27 16:23:44 -070049 // From android.content.ContentProviderOperation
50 public static final int TYPE_INSERT = 1;
51 public static final int TYPE_UPDATE = 2;
52 public static final int TYPE_DELETE = 3;
53 public static final int TYPE_ASSERT = 4;
54
Yorke Lee2644d942013-10-28 11:05:43 -070055 private static final long CONTACT_FIRST = 1;
56 private static final long CONTACT_SECOND = 2;
57
58 public static final long CONTACT_BOB = 10;
59 public static final long CONTACT_MARY = 11;
60
61 public static final long PHONE_RED = 20;
62 public static final long PHONE_GREEN = 21;
63 public static final long PHONE_BLUE = 22;
64
65 public static final long EMAIL_YELLOW = 25;
66
67 public static final long VER_FIRST = 100;
68 public static final long VER_SECOND = 200;
69
70 public static final String TEST_PHONE = "555-1212";
71 public static final String TEST_ACCOUNT = "org.example.test";
72
73 public RawContactDeltaListTests() {
74 super();
75 }
76
77 @Override
78 public void setUp() {
79 mContext = getContext();
80 }
81
82 /**
83 * Build a {@link AccountType} that has various odd constraints for
84 * testing purposes.
85 */
86 protected AccountType getAccountType() {
Gary Mai0a49afa2016-12-05 15:53:58 -080087 return new RawContactModifierTests.MockContactsSource();
Yorke Lee2644d942013-10-28 11:05:43 -070088 }
89
90 static ContentValues getValues(ContentProviderOperation operation)
91 throws NoSuchFieldException, IllegalAccessException {
92 final Field field = ContentProviderOperation.class.getDeclaredField("mValues");
93 field.setAccessible(true);
94 return (ContentValues) field.get(operation);
95 }
96
97 static RawContactDelta getUpdate(Context context, long rawContactId) {
98 final RawContact before = RawContactDeltaTests.getRawContact(context, rawContactId,
99 RawContactDeltaTests.TEST_PHONE_ID);
100 return RawContactDelta.fromBefore(before);
101 }
102
103 static RawContactDelta getInsert() {
104 final ContentValues after = new ContentValues();
105 after.put(RawContacts.ACCOUNT_NAME, RawContactDeltaTests.TEST_ACCOUNT_NAME);
106 after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
107
108 final ValuesDelta values = ValuesDelta.fromAfter(after);
109 return new RawContactDelta(values);
110 }
111
112 static RawContactDeltaList buildSet(RawContactDelta... deltas) {
113 final RawContactDeltaList set = new RawContactDeltaList();
114 Collections.addAll(set, deltas);
115 return set;
116 }
117
118 static RawContactDelta buildBeforeEntity(Context context, long rawContactId, long version,
119 ContentValues... entries) {
120 // Build an existing contact read from database
121 final ContentValues contact = new ContentValues();
122 contact.put(RawContacts.VERSION, version);
123 contact.put(RawContacts._ID, rawContactId);
124 final RawContact before = new RawContact(contact);
125 for (ContentValues entry : entries) {
126 before.addDataItemValues(entry);
127 }
128 return RawContactDelta.fromBefore(before);
129 }
130
131 static RawContactDelta buildAfterEntity(ContentValues... entries) {
132 // Build an existing contact read from database
133 final ContentValues contact = new ContentValues();
134 contact.put(RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT);
135 final RawContactDelta after = new RawContactDelta(ValuesDelta.fromAfter(contact));
136 for (ContentValues entry : entries) {
137 after.addEntry(ValuesDelta.fromAfter(entry));
138 }
139 return after;
140 }
141
142 static ContentValues buildPhone(long phoneId) {
143 return buildPhone(phoneId, Long.toString(phoneId));
144 }
145
146 static ContentValues buildPhone(long phoneId, String value) {
147 final ContentValues values = new ContentValues();
148 values.put(Data._ID, phoneId);
149 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
150 values.put(Phone.NUMBER, value);
151 values.put(Phone.TYPE, Phone.TYPE_HOME);
152 return values;
153 }
154
155 static ContentValues buildEmail(long emailId) {
156 final ContentValues values = new ContentValues();
157 values.put(Data._ID, emailId);
158 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
159 values.put(Email.DATA, Long.toString(emailId));
160 values.put(Email.TYPE, Email.TYPE_HOME);
161 return values;
162 }
163
164 static void insertPhone(RawContactDeltaList set, long rawContactId, ContentValues values) {
165 final RawContactDelta match = set.getByRawContactId(rawContactId);
166 match.addEntry(ValuesDelta.fromAfter(values));
167 }
168
169 static ValuesDelta getPhone(RawContactDeltaList set, long rawContactId, long dataId) {
170 final RawContactDelta match = set.getByRawContactId(rawContactId);
171 return match.getEntry(dataId);
172 }
173
Wenyi Wang009d63c2016-02-18 16:45:08 -0800174 static void assertDiffPattern(RawContactDelta delta, CPOWrapper... pattern) {
175 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
176 delta.buildAssertWrapper(diff);
177 delta.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700178 assertDiffPattern(diff, pattern);
179 }
180
Wenyi Wang009d63c2016-02-18 16:45:08 -0800181 static void assertDiffPattern(RawContactDeltaList set, CPOWrapper... pattern) {
182 assertDiffPattern(set.buildDiffWrapper(), pattern);
Yorke Lee2644d942013-10-28 11:05:43 -0700183 }
184
Wenyi Wang009d63c2016-02-18 16:45:08 -0800185 static void assertDiffPattern(ArrayList<CPOWrapper> diff, CPOWrapper... pattern) {
Yorke Lee2644d942013-10-28 11:05:43 -0700186 assertEquals("Unexpected operations", pattern.length, diff.size());
187 for (int i = 0; i < pattern.length; i++) {
Wenyi Wang009d63c2016-02-18 16:45:08 -0800188 final CPOWrapper expected = pattern[i];
189 final CPOWrapper found = diff.get(i);
Yorke Lee2644d942013-10-28 11:05:43 -0700190
Wenyi Wang009d63c2016-02-18 16:45:08 -0800191 assertEquals("Unexpected uri",
192 expected.getOperation().getUri(), found.getOperation().getUri());
Yorke Lee2644d942013-10-28 11:05:43 -0700193
Jay Shrauner7bae0632015-08-27 16:23:44 -0700194 final String expectedType = getTypeString(expected);
195 final String foundType = getTypeString(found);
Yorke Lee2644d942013-10-28 11:05:43 -0700196 assertEquals("Unexpected type", expectedType, foundType);
197
Wenyi Wang009d63c2016-02-18 16:45:08 -0800198 if (CompatUtils.isDeleteCompat(expected)) continue;
Yorke Lee2644d942013-10-28 11:05:43 -0700199
200 try {
Wenyi Wang009d63c2016-02-18 16:45:08 -0800201 final ContentValues expectedValues = getValues(expected.getOperation());
202 final ContentValues foundValues = getValues(found.getOperation());
Yorke Lee2644d942013-10-28 11:05:43 -0700203
204 expectedValues.remove(BaseColumns._ID);
205 foundValues.remove(BaseColumns._ID);
206
207 assertEquals("Unexpected values", expectedValues, foundValues);
208 } catch (NoSuchFieldException e) {
209 fail(e.toString());
210 } catch (IllegalAccessException e) {
211 fail(e.toString());
212 }
213 }
214 }
215
Wenyi Wang009d63c2016-02-18 16:45:08 -0800216 static String getTypeString(CPOWrapper cpoWrapper) {
217 if (CompatUtils.isAssertQueryCompat(cpoWrapper)) {
Jay Shrauner7bae0632015-08-27 16:23:44 -0700218 return "TYPE_ASSERT";
Wenyi Wang009d63c2016-02-18 16:45:08 -0800219 } else if (CompatUtils.isInsertCompat(cpoWrapper)) {
Jay Shrauner7bae0632015-08-27 16:23:44 -0700220 return "TYPE_INSERT";
Wenyi Wang009d63c2016-02-18 16:45:08 -0800221 } else if (CompatUtils.isUpdateCompat(cpoWrapper)) {
Jay Shrauner7bae0632015-08-27 16:23:44 -0700222 return "TYPE_UPDATE";
Wenyi Wang009d63c2016-02-18 16:45:08 -0800223 } else if (CompatUtils.isDeleteCompat(cpoWrapper)) {
Jay Shrauner7bae0632015-08-27 16:23:44 -0700224 return "TYPE_DELETE";
Yorke Lee2644d942013-10-28 11:05:43 -0700225 }
Jay Shrauner7bae0632015-08-27 16:23:44 -0700226 return "TYPE_UNKNOWN";
Yorke Lee2644d942013-10-28 11:05:43 -0700227 }
228
Wenyi Wang009d63c2016-02-18 16:45:08 -0800229 static CPOWrapper buildAssertVersion(long version) {
Yorke Lee2644d942013-10-28 11:05:43 -0700230 final ContentValues values = new ContentValues();
231 values.put(RawContacts.VERSION, version);
Wenyi Wang009d63c2016-02-18 16:45:08 -0800232 return buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_ASSERT, values);
Yorke Lee2644d942013-10-28 11:05:43 -0700233 }
234
Wenyi Wang009d63c2016-02-18 16:45:08 -0800235 static CPOWrapper buildAggregationModeUpdate(int mode) {
Yorke Lee2644d942013-10-28 11:05:43 -0700236 final ContentValues values = new ContentValues();
237 values.put(RawContacts.AGGREGATION_MODE, mode);
Wenyi Wang009d63c2016-02-18 16:45:08 -0800238 return buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_UPDATE, values);
Yorke Lee2644d942013-10-28 11:05:43 -0700239 }
240
Wenyi Wang009d63c2016-02-18 16:45:08 -0800241 static CPOWrapper buildUpdateAggregationSuspended() {
Yorke Lee2644d942013-10-28 11:05:43 -0700242 return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_SUSPENDED);
243 }
244
Wenyi Wang009d63c2016-02-18 16:45:08 -0800245 static CPOWrapper buildUpdateAggregationDefault() {
Yorke Lee2644d942013-10-28 11:05:43 -0700246 return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT);
247 }
248
Wenyi Wang009d63c2016-02-18 16:45:08 -0800249 static CPOWrapper buildUpdateAggregationKeepTogether(long rawContactId) {
Yorke Lee2644d942013-10-28 11:05:43 -0700250 final ContentValues values = new ContentValues();
251 values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
252 values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
Wenyi Wang009d63c2016-02-18 16:45:08 -0800253 return buildCPOWrapper(AggregationExceptions.CONTENT_URI, TYPE_UPDATE, values);
Yorke Lee2644d942013-10-28 11:05:43 -0700254 }
255
256 static ContentValues buildDataInsert(ValuesDelta values, long rawContactId) {
257 final ContentValues insertValues = values.getCompleteValues();
258 insertValues.put(Data.RAW_CONTACT_ID, rawContactId);
259 return insertValues;
260 }
261
Wenyi Wang009d63c2016-02-18 16:45:08 -0800262 static CPOWrapper buildDelete(Uri uri) {
263 return buildCPOWrapper(uri, TYPE_DELETE, (ContentValues) null);
Yorke Lee2644d942013-10-28 11:05:43 -0700264 }
265
266 static ContentProviderOperation buildOper(Uri uri, int type, ValuesDelta values) {
267 return buildOper(uri, type, values.getCompleteValues());
268 }
269
270 static ContentProviderOperation buildOper(Uri uri, int type, ContentValues values) {
271 switch (type) {
272 case TYPE_ASSERT:
273 return ContentProviderOperation.newAssertQuery(uri).withValues(values).build();
274 case TYPE_INSERT:
275 return ContentProviderOperation.newInsert(uri).withValues(values).build();
276 case TYPE_UPDATE:
277 return ContentProviderOperation.newUpdate(uri).withValues(values).build();
278 case TYPE_DELETE:
279 return ContentProviderOperation.newDelete(uri).build();
280 }
281 return null;
282 }
283
Wenyi Wang009d63c2016-02-18 16:45:08 -0800284 static CPOWrapper buildCPOWrapper(Uri uri, int type, ContentValues values) {
285 if (type == TYPE_ASSERT || type == TYPE_INSERT || type == TYPE_UPDATE
286 || type == TYPE_DELETE) {
287 return new CPOWrapper(buildOper(uri, type, values), type);
288 }
289 return null;
290 }
291
Yorke Lee2644d942013-10-28 11:05:43 -0700292 static Long getVersion(RawContactDeltaList set, Long rawContactId) {
293 return set.getByRawContactId(rawContactId).getValues().getAsLong(RawContacts.VERSION);
294 }
295
296 /**
297 * Count number of {@link AggregationExceptions} updates contained in the
Wenyi Wangf46a6192016-02-18 16:34:37 -0800298 * given list of {@link CPOWrapper}.
Yorke Lee2644d942013-10-28 11:05:43 -0700299 */
Wenyi Wangf46a6192016-02-18 16:34:37 -0800300 static int countExceptionUpdates(ArrayList<CPOWrapper> diff) {
Yorke Lee2644d942013-10-28 11:05:43 -0700301 int updateCount = 0;
Wenyi Wangf46a6192016-02-18 16:34:37 -0800302 for (CPOWrapper cpoWrapper : diff) {
303 final ContentProviderOperation oper = cpoWrapper.getOperation();
Yorke Lee2644d942013-10-28 11:05:43 -0700304 if (AggregationExceptions.CONTENT_URI.equals(oper.getUri())
Wenyi Wangf46a6192016-02-18 16:34:37 -0800305 && CompatUtils.isUpdateCompat(cpoWrapper)) {
Yorke Lee2644d942013-10-28 11:05:43 -0700306 updateCount++;
307 }
308 }
309 return updateCount;
310 }
311
312 public void testInsert() {
313 final RawContactDelta insert = getInsert();
314 final RawContactDeltaList set = buildSet(insert);
315
316 // Inserting single shouldn't create rules
Wenyi Wangf46a6192016-02-18 16:34:37 -0800317 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700318 final int exceptionCount = countExceptionUpdates(diff);
319 assertEquals("Unexpected exception updates", 0, exceptionCount);
320 }
321
322 public void testUpdateUpdate() {
323 final RawContactDelta updateFirst = getUpdate(mContext, CONTACT_FIRST);
324 final RawContactDelta updateSecond = getUpdate(mContext, CONTACT_SECOND);
325 final RawContactDeltaList set = buildSet(updateFirst, updateSecond);
326
327 // Updating two existing shouldn't create rules
Wenyi Wangf46a6192016-02-18 16:34:37 -0800328 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700329 final int exceptionCount = countExceptionUpdates(diff);
330 assertEquals("Unexpected exception updates", 0, exceptionCount);
331 }
332
333 public void testUpdateInsert() {
334 final RawContactDelta update = getUpdate(mContext, CONTACT_FIRST);
335 final RawContactDelta insert = getInsert();
336 final RawContactDeltaList set = buildSet(update, insert);
337
338 // New insert should only create one rule
Wenyi Wangf46a6192016-02-18 16:34:37 -0800339 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700340 final int exceptionCount = countExceptionUpdates(diff);
341 assertEquals("Unexpected exception updates", 1, exceptionCount);
342 }
343
344 public void testInsertUpdateInsert() {
345 final RawContactDelta insertFirst = getInsert();
346 final RawContactDelta update = getUpdate(mContext, CONTACT_FIRST);
347 final RawContactDelta insertSecond = getInsert();
348 final RawContactDeltaList set = buildSet(insertFirst, update, insertSecond);
349
350 // Two inserts should create two rules to bind against single existing
Wenyi Wangf46a6192016-02-18 16:34:37 -0800351 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700352 final int exceptionCount = countExceptionUpdates(diff);
353 assertEquals("Unexpected exception updates", 2, exceptionCount);
354 }
355
356 public void testInsertInsertInsert() {
357 final RawContactDelta insertFirst = getInsert();
358 final RawContactDelta insertSecond = getInsert();
359 final RawContactDelta insertThird = getInsert();
360 final RawContactDeltaList set = buildSet(insertFirst, insertSecond, insertThird);
361
362 // Three new inserts should create only two binding rules
Wenyi Wangf46a6192016-02-18 16:34:37 -0800363 final ArrayList<CPOWrapper> diff = set.buildDiffWrapper();
Yorke Lee2644d942013-10-28 11:05:43 -0700364 final int exceptionCount = countExceptionUpdates(diff);
365 assertEquals("Unexpected exception updates", 2, exceptionCount);
366 }
367
368 public void testMergeDataRemoteInsert() {
369 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
370 VER_FIRST, buildPhone(PHONE_RED)));
371 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
372 VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
373
374 // Merge in second version, verify they match
375 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
376 assertEquals("Unexpected change when merging", second, merged);
377 }
378
379 public void testMergeDataLocalUpdateRemoteInsert() {
380 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
381 VER_FIRST, buildPhone(PHONE_RED)));
382 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
383 VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
384
385 // Change the local number to trigger update
386 final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
387 phone.put(Phone.NUMBER, TEST_PHONE);
388
389 assertDiffPattern(first,
390 buildAssertVersion(VER_FIRST),
391 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800392 buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
Yorke Lee2644d942013-10-28 11:05:43 -0700393 buildUpdateAggregationDefault());
394
395 // Merge in the second version, verify diff matches
396 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
397 assertDiffPattern(merged,
398 buildAssertVersion(VER_SECOND),
399 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800400 buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
Yorke Lee2644d942013-10-28 11:05:43 -0700401 buildUpdateAggregationDefault());
402 }
403
404 public void testMergeDataLocalUpdateRemoteDelete() {
405 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
406 VER_FIRST, buildPhone(PHONE_RED)));
407 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
408 VER_SECOND, buildPhone(PHONE_GREEN)));
409
410 // Change the local number to trigger update
411 final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
412 phone.put(Phone.NUMBER, TEST_PHONE);
413
414 assertDiffPattern(first,
415 buildAssertVersion(VER_FIRST),
416 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800417 buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
Yorke Lee2644d942013-10-28 11:05:43 -0700418 buildUpdateAggregationDefault());
419
420 // Merge in the second version, verify that our update changed to
421 // insert, since RED was deleted on remote side
422 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
423 assertDiffPattern(merged,
424 buildAssertVersion(VER_SECOND),
425 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800426 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(phone, CONTACT_BOB)),
Yorke Lee2644d942013-10-28 11:05:43 -0700427 buildUpdateAggregationDefault());
428 }
429
430 public void testMergeDataLocalDeleteRemoteUpdate() {
431 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
432 VER_FIRST, buildPhone(PHONE_RED)));
433 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
434 VER_SECOND, buildPhone(PHONE_RED, TEST_PHONE)));
435
436 // Delete phone locally
437 final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
438 phone.markDeleted();
439
440 assertDiffPattern(first,
441 buildAssertVersion(VER_FIRST),
442 buildUpdateAggregationSuspended(),
443 buildDelete(Data.CONTENT_URI),
444 buildUpdateAggregationDefault());
445
446 // Merge in the second version, verify that our delete remains
447 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
448 assertDiffPattern(merged,
449 buildAssertVersion(VER_SECOND),
450 buildUpdateAggregationSuspended(),
451 buildDelete(Data.CONTENT_URI),
452 buildUpdateAggregationDefault());
453 }
454
455 public void testMergeDataLocalInsertRemoteInsert() {
456 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
457 VER_FIRST, buildPhone(PHONE_RED)));
458 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
459 VER_SECOND, buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
460
461 // Insert new phone locally
462 final ValuesDelta bluePhone = ValuesDelta.fromAfter(buildPhone(PHONE_BLUE));
463 first.getByRawContactId(CONTACT_BOB).addEntry(bluePhone);
464 assertDiffPattern(first,
465 buildAssertVersion(VER_FIRST),
466 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800467 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)),
Yorke Lee2644d942013-10-28 11:05:43 -0700468 buildUpdateAggregationDefault());
469
470 // Merge in the second version, verify that our insert remains
471 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
472 assertDiffPattern(merged,
473 buildAssertVersion(VER_SECOND),
474 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800475 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)),
Yorke Lee2644d942013-10-28 11:05:43 -0700476 buildUpdateAggregationDefault());
477 }
478
479 public void testMergeRawContactLocalInsertRemoteInsert() {
480 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
481 VER_FIRST, buildPhone(PHONE_RED)));
482 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
483 VER_SECOND, buildPhone(PHONE_RED)), buildBeforeEntity(mContext, CONTACT_MARY,
484 VER_SECOND, buildPhone(PHONE_RED)));
485
486 // Add new contact locally, should remain insert
487 final ContentValues joePhoneInsert = buildPhone(PHONE_BLUE);
488 final RawContactDelta joeContact = buildAfterEntity(joePhoneInsert);
489 final ContentValues joeContactInsert = joeContact.getValues().getCompleteValues();
490 joeContactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
491 first.add(joeContact);
492 assertDiffPattern(first,
493 buildAssertVersion(VER_FIRST),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800494 buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert),
495 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert),
Yorke Lee2644d942013-10-28 11:05:43 -0700496 buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
497 buildUpdateAggregationKeepTogether(CONTACT_BOB));
498
499 // Merge in the second version, verify that our insert remains
500 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
501 assertDiffPattern(merged,
502 buildAssertVersion(VER_SECOND),
503 buildAssertVersion(VER_SECOND),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800504 buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert),
505 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert),
Yorke Lee2644d942013-10-28 11:05:43 -0700506 buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
507 buildUpdateAggregationKeepTogether(CONTACT_BOB));
508 }
509
510 public void testMergeRawContactLocalDeleteRemoteDelete() {
511 final RawContactDeltaList first = buildSet(
512 buildBeforeEntity(mContext, CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
513 buildBeforeEntity(mContext, CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
514 final RawContactDeltaList second = buildSet(
515 buildBeforeEntity(mContext, CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
516
517 // Remove contact locally
518 first.getByRawContactId(CONTACT_MARY).markDeleted();
519 assertDiffPattern(first,
520 buildAssertVersion(VER_FIRST),
521 buildAssertVersion(VER_FIRST),
522 buildDelete(RawContacts.CONTENT_URI));
523
524 // Merge in the second version, verify that our delete isn't needed
525 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
526 assertDiffPattern(merged);
527 }
528
529 public void testMergeRawContactLocalUpdateRemoteDelete() {
530 final RawContactDeltaList first = buildSet(
531 buildBeforeEntity(mContext, CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
532 buildBeforeEntity(mContext, CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
533 final RawContactDeltaList second = buildSet(
534 buildBeforeEntity(mContext, CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
535
536 // Perform local update
537 final ValuesDelta phone = getPhone(first, CONTACT_MARY, PHONE_RED);
538 phone.put(Phone.NUMBER, TEST_PHONE);
539 assertDiffPattern(first,
540 buildAssertVersion(VER_FIRST),
541 buildAssertVersion(VER_FIRST),
542 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800543 buildCPOWrapper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
Yorke Lee2644d942013-10-28 11:05:43 -0700544 buildUpdateAggregationDefault());
545
546 final ContentValues phoneInsert = phone.getCompleteValues();
547 final ContentValues contactInsert = first.getByRawContactId(CONTACT_MARY).getValues()
548 .getCompleteValues();
549 contactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
550
551 // Merge and verify that update turned into insert
552 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
553 assertDiffPattern(merged,
554 buildAssertVersion(VER_SECOND),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800555 buildCPOWrapper(RawContacts.CONTENT_URI, TYPE_INSERT, contactInsert),
556 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, phoneInsert),
Yorke Lee2644d942013-10-28 11:05:43 -0700557 buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
558 buildUpdateAggregationKeepTogether(CONTACT_BOB));
559 }
560
561 public void testMergeUsesNewVersion() {
562 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
563 VER_FIRST, buildPhone(PHONE_RED)));
564 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
565 VER_SECOND, buildPhone(PHONE_RED)));
566
567 assertEquals((Long)VER_FIRST, getVersion(first, CONTACT_BOB));
568 assertEquals((Long)VER_SECOND, getVersion(second, CONTACT_BOB));
569
570 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
571 assertEquals((Long)VER_SECOND, getVersion(merged, CONTACT_BOB));
572 }
573
574 public void testMergeAfterEnsureAndTrim() {
575 final RawContactDeltaList first = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
576 VER_FIRST, buildEmail(EMAIL_YELLOW)));
577 final RawContactDeltaList second = buildSet(buildBeforeEntity(mContext, CONTACT_BOB,
578 VER_SECOND, buildEmail(EMAIL_YELLOW)));
579
580 // Ensure we have at least one phone
581 final AccountType source = getAccountType();
582 final RawContactDelta bobContact = first.getByRawContactId(CONTACT_BOB);
583 RawContactModifier.ensureKindExists(bobContact, source, Phone.CONTENT_ITEM_TYPE);
584 final ValuesDelta bobPhone = bobContact.getSuperPrimaryEntry(Phone.CONTENT_ITEM_TYPE, true);
585
586 // Make sure the update would insert a row
587 assertDiffPattern(first,
588 buildAssertVersion(VER_FIRST),
589 buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800590 buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bobPhone, CONTACT_BOB)),
Yorke Lee2644d942013-10-28 11:05:43 -0700591 buildUpdateAggregationDefault());
592
593 // Trim values and ensure that we don't insert things
594 RawContactModifier.trimEmpty(bobContact, source);
595 assertDiffPattern(first);
596
597 // Now re-parent the change, which should remain no-op
598 final RawContactDeltaList merged = RawContactDeltaList.mergeAfter(second, first);
599 assertDiffPattern(merged);
600 }
601}