blob: 594bb443fccf8ef2ff7dd705cb2b90037997dac6 [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;
Yorke Lee2644d942013-10-28 11:05:43 -070021import android.os.Bundle;
Yorke Lee2644d942013-10-28 11:05:43 -070022import android.provider.ContactsContract.CommonDataKinds.Email;
23import android.provider.ContactsContract.CommonDataKinds.Event;
24import android.provider.ContactsContract.CommonDataKinds.Im;
25import android.provider.ContactsContract.CommonDataKinds.Organization;
26import android.provider.ContactsContract.CommonDataKinds.Phone;
27import android.provider.ContactsContract.CommonDataKinds.StructuredName;
28import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
29import android.provider.ContactsContract.Data;
30import android.provider.ContactsContract.Intents.Insert;
31import android.provider.ContactsContract.RawContacts;
32import android.test.AndroidTestCase;
33import android.test.suitebuilder.annotation.LargeTest;
34
Gary Mai0a49afa2016-12-05 15:53:58 -080035import com.android.contacts.R;
Gary Mai69c182a2016-12-05 13:07:03 -080036import com.android.contacts.compat.CompatUtils;
Gary Mai69c182a2016-12-05 13:07:03 -080037import com.android.contacts.model.account.AccountType;
38import com.android.contacts.model.account.AccountType.EditType;
39import com.android.contacts.model.account.ExchangeAccountType;
40import com.android.contacts.model.account.GoogleAccountType;
41import com.android.contacts.model.dataitem.DataKind;
Gary Mai0a49afa2016-12-05 15:53:58 -080042import com.android.contacts.test.mocks.ContactsMockContext;
43import com.android.contacts.test.mocks.MockAccountTypeManager;
Gary Mai69c182a2016-12-05 13:07:03 -080044
Yorke Lee2644d942013-10-28 11:05:43 -070045import com.google.common.collect.Lists;
46
47import java.util.ArrayList;
48import java.util.List;
49
50/**
51 * Tests for {@link RawContactModifier} to verify that {@link AccountType}
52 * constraints are being enforced correctly.
53 */
54@LargeTest
55public class RawContactModifierTests extends AndroidTestCase {
56 public static final String TAG = "EntityModifierTests";
57
Jay Shrauner7bae0632015-08-27 16:23:44 -070058 // From android.content.ContentProviderOperation
59 public static final int TYPE_INSERT = 1;
60
Yorke Lee2644d942013-10-28 11:05:43 -070061 public static final long VER_FIRST = 100;
62
63 private static final long TEST_ID = 4;
64 private static final String TEST_PHONE = "218-555-1212";
65 private static final String TEST_NAME = "Adam Young";
66 private static final String TEST_NAME2 = "Breanne Duren";
67 private static final String TEST_IM = "example@example.com";
68 private static final String TEST_POSTAL = "1600 Amphitheatre Parkway";
69
70 private static final String TEST_ACCOUNT_NAME = "unittest@example.com";
71 private static final String TEST_ACCOUNT_TYPE = "com.example.unittest";
72
73 private static final String EXCHANGE_ACCT_TYPE = "com.android.exchange";
74
75 @Override
76 public void setUp() {
77 mContext = getContext();
78 }
79
80 public static class MockContactsSource extends AccountType {
81
82 MockContactsSource() {
83 try {
84 this.accountType = TEST_ACCOUNT_TYPE;
85
86 final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
87 R.string.nameLabelsGroup, -1, true);
88 nameKind.typeOverallMax = 1;
89 addKind(nameKind);
90
91 // Phone allows maximum 2 home, 1 work, and unlimited other, with
92 // constraint of 5 numbers maximum.
93 final DataKind phoneKind = new DataKind(
94 Phone.CONTENT_ITEM_TYPE, -1, 10, true);
95
96 phoneKind.typeOverallMax = 5;
97 phoneKind.typeColumn = Phone.TYPE;
98 phoneKind.typeList = Lists.newArrayList();
99 phoneKind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2));
100 phoneKind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1));
101 phoneKind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true));
102 phoneKind.typeList.add(new EditType(Phone.TYPE_OTHER, -1));
103
104 phoneKind.fieldList = Lists.newArrayList();
105 phoneKind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
106 phoneKind.fieldList.add(new EditField(Phone.LABEL, -1, -1));
107
108 addKind(phoneKind);
109
110 // Email is unlimited
111 final DataKind emailKind = new DataKind(Email.CONTENT_ITEM_TYPE, -1, 10, true);
112 emailKind.typeOverallMax = -1;
113 emailKind.fieldList = Lists.newArrayList();
114 emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
115 addKind(emailKind);
116
117 // IM is only one
118 final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10, true);
119 imKind.typeOverallMax = 1;
120 imKind.fieldList = Lists.newArrayList();
121 imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
122 addKind(imKind);
123
124 // Organization is only one
125 final DataKind orgKind = new DataKind(Organization.CONTENT_ITEM_TYPE, -1, 10, true);
126 orgKind.typeOverallMax = 1;
127 orgKind.fieldList = Lists.newArrayList();
128 orgKind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
129 orgKind.fieldList.add(new EditField(Organization.TITLE, -1, -1));
130 addKind(orgKind);
131 } catch (DefinitionException e) {
132 throw new RuntimeException(e);
133 }
134 }
135
136 @Override
137 public boolean isGroupMembershipEditable() {
138 return false;
139 }
140
141 @Override
142 public boolean areContactsWritable() {
143 return true;
144 }
145 }
146
147 /**
148 * Build a {@link AccountType} that has various odd constraints for
149 * testing purposes.
150 */
151 protected AccountType getAccountType() {
152 return new MockContactsSource();
153 }
154
155 /**
156 * Build {@link AccountTypeManager} instance.
157 */
158 protected AccountTypeManager getAccountTypes(AccountType... types) {
159 return new MockAccountTypeManager(types, null);
160 }
161
162 /**
163 * Build an {@link RawContact} with the requested set of phone numbers.
164 */
165 protected RawContactDelta getRawContact(Long existingId, ContentValues... entries) {
166 final ContentValues contact = new ContentValues();
167 if (existingId != null) {
168 contact.put(RawContacts._ID, existingId);
169 }
170 contact.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME);
171 contact.put(RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE);
172
173 final RawContact before = new RawContact(contact);
174 for (ContentValues values : entries) {
175 before.addDataItemValues(values);
176 }
177 return RawContactDelta.fromBefore(before);
178 }
179
180 /**
181 * Assert this {@link List} contains the given {@link Object}.
182 */
183 protected void assertContains(List<?> list, Object object) {
184 assertTrue("Missing expected value", list.contains(object));
185 }
186
187 /**
188 * Assert this {@link List} does not contain the given {@link Object}.
189 */
190 protected void assertNotContains(List<?> list, Object object) {
191 assertFalse("Contained unexpected value", list.contains(object));
192 }
193
194 /**
195 * Insert various rows to test
196 * {@link RawContactModifier#getValidTypes(RawContactDelta, DataKind, EditType)}
197 */
198 public void testValidTypes() {
199 // Build a source and pull specific types
200 final AccountType source = getAccountType();
201 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
202 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
203 final EditType typeWork = RawContactModifier.getType(kindPhone, Phone.TYPE_WORK);
204 final EditType typeOther = RawContactModifier.getType(kindPhone, Phone.TYPE_OTHER);
205
206 List<EditType> validTypes;
207
208 // Add first home, first work
209 final RawContactDelta state = getRawContact(TEST_ID);
210 RawContactModifier.insertChild(state, kindPhone, typeHome);
211 RawContactModifier.insertChild(state, kindPhone, typeWork);
212
213 // Expecting home, other
Tingting Wangd7e0d692015-12-10 14:46:51 -0800214 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true);
Yorke Lee2644d942013-10-28 11:05:43 -0700215 assertContains(validTypes, typeHome);
216 assertNotContains(validTypes, typeWork);
217 assertContains(validTypes, typeOther);
218
219 // Add second home
220 RawContactModifier.insertChild(state, kindPhone, typeHome);
221
222 // Expecting other
Tingting Wangd7e0d692015-12-10 14:46:51 -0800223 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true);
Yorke Lee2644d942013-10-28 11:05:43 -0700224 assertNotContains(validTypes, typeHome);
225 assertNotContains(validTypes, typeWork);
226 assertContains(validTypes, typeOther);
227
228 // Add third and fourth home (invalid, but possible)
229 RawContactModifier.insertChild(state, kindPhone, typeHome);
230 RawContactModifier.insertChild(state, kindPhone, typeHome);
231
232 // Expecting none
Tingting Wangd7e0d692015-12-10 14:46:51 -0800233 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true);
Yorke Lee2644d942013-10-28 11:05:43 -0700234 assertNotContains(validTypes, typeHome);
235 assertNotContains(validTypes, typeWork);
236 assertNotContains(validTypes, typeOther);
237 }
238
239 /**
Tingting Wangd7e0d692015-12-10 14:46:51 -0800240 * Test which valid types there are when trying to update the editor type.
241 * {@link RawContactModifier#getValidTypes(RawContactDelta, DataKind, EditType, Boolean)}
242 */
243 public void testValidTypesWhenUpdating() {
244 // Build a source and pull specific types
245 final AccountType source = getAccountType();
246 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
247 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
248 final EditType typeWork = RawContactModifier.getType(kindPhone, Phone.TYPE_WORK);
249 final EditType typeOther = RawContactModifier.getType(kindPhone, Phone.TYPE_OTHER);
250
251 List<EditType> validTypes;
252
253 // Add first home, first work
254 final RawContactDelta state = getRawContact(TEST_ID);
255 RawContactModifier.insertChild(state, kindPhone, typeHome);
256 RawContactModifier.insertChild(state, kindPhone, typeWork);
257
258 // Update editor type for home.
259 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, false);
260 assertContains(validTypes, typeHome);
261 assertNotContains(validTypes, typeWork);
262 assertContains(validTypes, typeOther);
263
264 // Add another 3 types. Overall limit is 5.
265 RawContactModifier.insertChild(state, kindPhone, typeHome);
266 RawContactModifier.insertChild(state, kindPhone, typeOther);
267 RawContactModifier.insertChild(state, kindPhone, typeOther);
268
269 // "Other" is valid when updating the editor type.
270 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, false);
271 assertNotContains(validTypes, typeHome);
272 assertNotContains(validTypes, typeWork);
273 assertContains(validTypes, typeOther);
274 }
275
276 /**
Yorke Lee2644d942013-10-28 11:05:43 -0700277 * Test {@link RawContactModifier#canInsert(RawContactDelta, DataKind)} by
278 * inserting various rows.
279 */
280 public void testCanInsert() {
281 // Build a source and pull specific types
282 final AccountType source = getAccountType();
283 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
284 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
285 final EditType typeWork = RawContactModifier.getType(kindPhone, Phone.TYPE_WORK);
286 final EditType typeOther = RawContactModifier.getType(kindPhone, Phone.TYPE_OTHER);
287
288 // Add first home, first work
289 final RawContactDelta state = getRawContact(TEST_ID);
290 RawContactModifier.insertChild(state, kindPhone, typeHome);
291 RawContactModifier.insertChild(state, kindPhone, typeWork);
292 assertTrue("Unable to insert", RawContactModifier.canInsert(state, kindPhone));
293
294 // Add two other, which puts us just under "5" overall limit
295 RawContactModifier.insertChild(state, kindPhone, typeOther);
296 RawContactModifier.insertChild(state, kindPhone, typeOther);
297 assertTrue("Unable to insert", RawContactModifier.canInsert(state, kindPhone));
298
299 // Add second home, which should push to snug limit
300 RawContactModifier.insertChild(state, kindPhone, typeHome);
301 assertFalse("Able to insert", RawContactModifier.canInsert(state, kindPhone));
302 }
303
304 /**
305 * Test
306 * {@link RawContactModifier#getBestValidType(RawContactDelta, DataKind, boolean, int)}
307 * by asserting expected best options in various states.
308 */
309 public void testBestValidType() {
310 // Build a source and pull specific types
311 final AccountType source = getAccountType();
312 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
313 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
314 final EditType typeWork = RawContactModifier.getType(kindPhone, Phone.TYPE_WORK);
315 final EditType typeFaxWork = RawContactModifier.getType(kindPhone, Phone.TYPE_FAX_WORK);
316 final EditType typeOther = RawContactModifier.getType(kindPhone, Phone.TYPE_OTHER);
317
318 EditType suggested;
319
320 // Default suggestion should be home
321 final RawContactDelta state = getRawContact(TEST_ID);
322 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE);
323 assertEquals("Unexpected suggestion", typeHome, suggested);
324
325 // Add first home, should now suggest work
326 RawContactModifier.insertChild(state, kindPhone, typeHome);
327 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE);
328 assertEquals("Unexpected suggestion", typeWork, suggested);
329
330 // Add work fax, should still suggest work
331 RawContactModifier.insertChild(state, kindPhone, typeFaxWork);
332 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE);
333 assertEquals("Unexpected suggestion", typeWork, suggested);
334
335 // Add other, should still suggest work
336 RawContactModifier.insertChild(state, kindPhone, typeOther);
337 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE);
338 assertEquals("Unexpected suggestion", typeWork, suggested);
339
340 // Add work, now should suggest other
341 RawContactModifier.insertChild(state, kindPhone, typeWork);
342 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE);
343 assertEquals("Unexpected suggestion", typeOther, suggested);
344 }
345
346 public void testIsEmptyEmpty() {
347 final AccountType source = getAccountType();
348 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
349
350 // Test entirely empty row
351 final ContentValues after = new ContentValues();
352 final ValuesDelta values = ValuesDelta.fromAfter(after);
353
354 assertTrue("Expected empty", RawContactModifier.isEmpty(values, kindPhone));
355 }
356
357 public void testIsEmptyDirectFields() {
358 final AccountType source = getAccountType();
359 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
360 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
361
362 // Test row that has type values, but core fields are empty
363 final RawContactDelta state = getRawContact(TEST_ID);
364 final ValuesDelta values = RawContactModifier.insertChild(state, kindPhone, typeHome);
365
366 assertTrue("Expected empty", RawContactModifier.isEmpty(values, kindPhone));
367
368 // Insert some data to trigger non-empty state
369 values.put(Phone.NUMBER, TEST_PHONE);
370
371 assertFalse("Expected non-empty", RawContactModifier.isEmpty(values, kindPhone));
372 }
373
374 public void testTrimEmptySingle() {
375 final AccountType source = getAccountType();
376 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
377 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
378
379 // Test row that has type values, but core fields are empty
380 final RawContactDelta state = getRawContact(TEST_ID);
381 RawContactModifier.insertChild(state, kindPhone, typeHome);
382
383 // Build diff, expecting insert for data row and update enforcement
Wenyi Wangf46a6192016-02-18 16:34:37 -0800384 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
385 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700386 assertEquals("Unexpected operations", 3, diff.size());
387 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800388 final CPOWrapper cpoWrapper = diff.get(0);
389 final ContentProviderOperation oper = cpoWrapper.getOperation();
390 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700391 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
392 }
393 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800394 final CPOWrapper cpoWrapper = diff.get(1);
395 final ContentProviderOperation oper = cpoWrapper.getOperation();
396 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700397 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
398 }
399 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800400 final CPOWrapper cpoWrapper = diff.get(2);
401 final ContentProviderOperation oper = cpoWrapper.getOperation();
402 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700403 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
404 }
405
406 // Trim empty rows and try again, expecting delete of overall contact
407 RawContactModifier.trimEmpty(state, source);
408 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800409 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700410 assertEquals("Unexpected operations", 1, diff.size());
411 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800412 final CPOWrapper cpoWrapper = diff.get(0);
413 final ContentProviderOperation oper = cpoWrapper.getOperation();
414 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700415 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
416 }
417 }
418
419 public void testTrimEmptySpaces() {
420 final AccountType source = getAccountType();
421 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
422 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
423
424 // Test row that has type values, but values are spaces
425 final RawContactDelta state = RawContactDeltaListTests.buildBeforeEntity(mContext, TEST_ID,
426 VER_FIRST);
427 final ValuesDelta values = RawContactModifier.insertChild(state, kindPhone, typeHome);
428 values.put(Phone.NUMBER, " ");
429
430 // Build diff, expecting insert for data row and update enforcement
431 RawContactDeltaListTests.assertDiffPattern(state,
432 RawContactDeltaListTests.buildAssertVersion(VER_FIRST),
433 RawContactDeltaListTests.buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800434 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT,
Yorke Lee2644d942013-10-28 11:05:43 -0700435 RawContactDeltaListTests.buildDataInsert(values, TEST_ID)),
436 RawContactDeltaListTests.buildUpdateAggregationDefault());
437
438 // Trim empty rows and try again, expecting delete of overall contact
439 RawContactModifier.trimEmpty(state, source);
440 RawContactDeltaListTests.assertDiffPattern(state,
441 RawContactDeltaListTests.buildAssertVersion(VER_FIRST),
442 RawContactDeltaListTests.buildDelete(RawContacts.CONTENT_URI));
443 }
444
445 public void testTrimLeaveValid() {
446 final AccountType source = getAccountType();
447 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
448 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
449
450 // Test row that has type values with valid number
451 final RawContactDelta state = RawContactDeltaListTests.buildBeforeEntity(mContext, TEST_ID,
452 VER_FIRST);
453 final ValuesDelta values = RawContactModifier.insertChild(state, kindPhone, typeHome);
454 values.put(Phone.NUMBER, TEST_PHONE);
455
456 // Build diff, expecting insert for data row and update enforcement
457 RawContactDeltaListTests.assertDiffPattern(state,
458 RawContactDeltaListTests.buildAssertVersion(VER_FIRST),
459 RawContactDeltaListTests.buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800460 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT,
Yorke Lee2644d942013-10-28 11:05:43 -0700461 RawContactDeltaListTests.buildDataInsert(values, TEST_ID)),
462 RawContactDeltaListTests.buildUpdateAggregationDefault());
463
464 // Trim empty rows and try again, expecting no differences
465 RawContactModifier.trimEmpty(state, source);
466 RawContactDeltaListTests.assertDiffPattern(state,
467 RawContactDeltaListTests.buildAssertVersion(VER_FIRST),
468 RawContactDeltaListTests.buildUpdateAggregationSuspended(),
Wenyi Wang009d63c2016-02-18 16:45:08 -0800469 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT,
Yorke Lee2644d942013-10-28 11:05:43 -0700470 RawContactDeltaListTests.buildDataInsert(values, TEST_ID)),
471 RawContactDeltaListTests.buildUpdateAggregationDefault());
472 }
473
474 public void testTrimEmptyUntouched() {
475 final AccountType source = getAccountType();
476 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
477 RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
478
479 // Build "before" that has empty row
480 final RawContactDelta state = getRawContact(TEST_ID);
481 final ContentValues before = new ContentValues();
482 before.put(Data._ID, TEST_ID);
483 before.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
484 state.addEntry(ValuesDelta.fromBefore(before));
485
486 // Build diff, expecting no changes
Wenyi Wangf46a6192016-02-18 16:34:37 -0800487 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
488 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700489 assertEquals("Unexpected operations", 0, diff.size());
490
491 // Try trimming existing empty, which we shouldn't touch
492 RawContactModifier.trimEmpty(state, source);
493 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800494 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700495 assertEquals("Unexpected operations", 0, diff.size());
496 }
497
498 public void testTrimEmptyAfterUpdate() {
499 final AccountType source = getAccountType();
500 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
501 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
502
503 // Build "before" that has row with some phone number
504 final ContentValues before = new ContentValues();
505 before.put(Data._ID, TEST_ID);
506 before.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
507 before.put(kindPhone.typeColumn, typeHome.rawValue);
508 before.put(Phone.NUMBER, TEST_PHONE);
509 final RawContactDelta state = getRawContact(TEST_ID, before);
510
511 // Build diff, expecting no changes
Wenyi Wangf46a6192016-02-18 16:34:37 -0800512 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
513 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700514 assertEquals("Unexpected operations", 0, diff.size());
515
516 // Now update row by changing number to empty string, expecting single update
517 final ValuesDelta child = state.getEntry(TEST_ID);
518 child.put(Phone.NUMBER, "");
519 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800520 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700521 assertEquals("Unexpected operations", 3, diff.size());
522 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800523 final CPOWrapper cpoWrapper = diff.get(0);
524 final ContentProviderOperation oper = cpoWrapper.getOperation();
525 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700526 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
527 }
528 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800529 final CPOWrapper cpoWrapper = diff.get(1);
530 final ContentProviderOperation oper = cpoWrapper.getOperation();
531 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700532 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
533 }
534 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800535 final CPOWrapper cpoWrapper = diff.get(2);
536 final ContentProviderOperation oper = cpoWrapper.getOperation();
537 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700538 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
539 }
540
541 // Now run trim, which should turn that update into delete
542 RawContactModifier.trimEmpty(state, source);
543 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800544 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700545 assertEquals("Unexpected operations", 1, diff.size());
546 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800547 final CPOWrapper cpoWrapper = diff.get(0);
548 final ContentProviderOperation oper = cpoWrapper.getOperation();
549 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700550 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
551 }
552 }
553
554 public void testTrimInsertEmpty() {
555 final AccountType accountType = getAccountType();
556 final AccountTypeManager accountTypes = getAccountTypes(accountType);
557 final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
558 RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
559
560 // Try creating a contact without any child entries
561 final RawContactDelta state = getRawContact(null);
562 final RawContactDeltaList set = new RawContactDeltaList();
563 set.add(state);
564
Yorke Lee2644d942013-10-28 11:05:43 -0700565 // Build diff, expecting single insert
Wenyi Wangf46a6192016-02-18 16:34:37 -0800566 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
567 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700568 assertEquals("Unexpected operations", 2, diff.size());
569 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800570 final CPOWrapper cpoWrapper = diff.get(0);
571 final ContentProviderOperation oper = cpoWrapper.getOperation();
572 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700573 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
574 }
575
576 // Trim empty rows and try again, expecting no insert
577 RawContactModifier.trimEmpty(set, accountTypes);
578 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800579 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700580 assertEquals("Unexpected operations", 0, diff.size());
581 }
582
583 public void testTrimInsertInsert() {
584 final AccountType accountType = getAccountType();
585 final AccountTypeManager accountTypes = getAccountTypes(accountType);
586 final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
587 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
588
589 // Try creating a contact with single empty entry
590 final RawContactDelta state = getRawContact(null);
591 RawContactModifier.insertChild(state, kindPhone, typeHome);
592 final RawContactDeltaList set = new RawContactDeltaList();
593 set.add(state);
594
595 // Build diff, expecting two insert operations
Wenyi Wangf46a6192016-02-18 16:34:37 -0800596 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
597 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700598 assertEquals("Unexpected operations", 3, diff.size());
599 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800600 final CPOWrapper cpoWrapper = diff.get(0);
601 final ContentProviderOperation oper = cpoWrapper.getOperation();
602 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700603 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
604 }
605 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800606 final CPOWrapper cpoWrapper = diff.get(1);
607 final ContentProviderOperation oper = cpoWrapper.getOperation();
608 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700609 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
610 }
611
612 // Trim empty rows and try again, expecting silence
613 RawContactModifier.trimEmpty(set, accountTypes);
614 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800615 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700616 assertEquals("Unexpected operations", 0, diff.size());
617 }
618
619 public void testTrimUpdateRemain() {
620 final AccountType accountType = getAccountType();
621 final AccountTypeManager accountTypes = getAccountTypes(accountType);
622 final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
623 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
624
625 // Build "before" with two phone numbers
626 final ContentValues first = new ContentValues();
627 first.put(Data._ID, TEST_ID);
628 first.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
629 first.put(kindPhone.typeColumn, typeHome.rawValue);
630 first.put(Phone.NUMBER, TEST_PHONE);
631
632 final ContentValues second = new ContentValues();
633 second.put(Data._ID, TEST_ID);
634 second.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
635 second.put(kindPhone.typeColumn, typeHome.rawValue);
636 second.put(Phone.NUMBER, TEST_PHONE);
637
638 final RawContactDelta state = getRawContact(TEST_ID, first, second);
639 final RawContactDeltaList set = new RawContactDeltaList();
640 set.add(state);
641
642 // Build diff, expecting no changes
Wenyi Wangf46a6192016-02-18 16:34:37 -0800643 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
644 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700645 assertEquals("Unexpected operations", 0, diff.size());
646
647 // Now update row by changing number to empty string, expecting single update
648 final ValuesDelta child = state.getEntry(TEST_ID);
649 child.put(Phone.NUMBER, "");
650 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800651 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700652 assertEquals("Unexpected operations", 3, diff.size());
653 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800654 final CPOWrapper cpoWrapper = diff.get(0);
655 final ContentProviderOperation oper = cpoWrapper.getOperation();
656 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700657 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
658 }
659 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800660 final CPOWrapper cpoWrapper = diff.get(1);
661 final ContentProviderOperation oper = cpoWrapper.getOperation();
662 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700663 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
664 }
665 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800666 final CPOWrapper cpoWrapper = diff.get(2);
667 final ContentProviderOperation oper = cpoWrapper.getOperation();
668 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700669 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
670 }
671
672 // Now run trim, which should turn that update into delete
673 RawContactModifier.trimEmpty(set, accountTypes);
674 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800675 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700676 assertEquals("Unexpected operations", 3, diff.size());
677 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800678 final CPOWrapper cpoWrapper = diff.get(0);
679 final ContentProviderOperation oper = cpoWrapper.getOperation();
680 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700681 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
682 }
683 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800684 final CPOWrapper cpoWrapper = diff.get(1);
685 final ContentProviderOperation oper = cpoWrapper.getOperation();
686 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700687 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
688 }
689 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800690 final CPOWrapper cpoWrapper = diff.get(2);
691 final ContentProviderOperation oper = cpoWrapper.getOperation();
692 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700693 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
694 }
695 }
696
697 public void testTrimUpdateUpdate() {
698 final AccountType accountType = getAccountType();
699 final AccountTypeManager accountTypes = getAccountTypes(accountType);
700 final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
701 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME);
702
703 // Build "before" with two phone numbers
704 final ContentValues first = new ContentValues();
705 first.put(Data._ID, TEST_ID);
706 first.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
707 first.put(kindPhone.typeColumn, typeHome.rawValue);
708 first.put(Phone.NUMBER, TEST_PHONE);
709
710 final RawContactDelta state = getRawContact(TEST_ID, first);
711 final RawContactDeltaList set = new RawContactDeltaList();
712 set.add(state);
713
714 // Build diff, expecting no changes
Wenyi Wangf46a6192016-02-18 16:34:37 -0800715 final ArrayList<CPOWrapper> diff = Lists.newArrayList();
716 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700717 assertEquals("Unexpected operations", 0, diff.size());
718
719 // Now update row by changing number to empty string, expecting single update
720 final ValuesDelta child = state.getEntry(TEST_ID);
721 child.put(Phone.NUMBER, "");
722 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800723 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700724 assertEquals("Unexpected operations", 3, diff.size());
725 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800726 final CPOWrapper cpoWrapper = diff.get(0);
727 final ContentProviderOperation oper = cpoWrapper.getOperation();
728 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700729 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
730 }
731 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800732 final CPOWrapper cpoWrapper = diff.get(1);
733 final ContentProviderOperation oper = cpoWrapper.getOperation();
734 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700735 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri());
736 }
737 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800738 final CPOWrapper cpoWrapper = diff.get(2);
739 final ContentProviderOperation oper = cpoWrapper.getOperation();
740 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700741 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
742 }
743
744 // Now run trim, which should turn into deleting the whole contact
745 RawContactModifier.trimEmpty(set, accountTypes);
746 diff.clear();
Wenyi Wangf46a6192016-02-18 16:34:37 -0800747 state.buildDiffWrapper(diff);
Yorke Lee2644d942013-10-28 11:05:43 -0700748 assertEquals("Unexpected operations", 1, diff.size());
749 {
Wenyi Wangf46a6192016-02-18 16:34:37 -0800750 final CPOWrapper cpoWrapper = diff.get(0);
751 final ContentProviderOperation oper = cpoWrapper.getOperation();
752 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper));
Yorke Lee2644d942013-10-28 11:05:43 -0700753 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri());
754 }
755 }
756
757 public void testParseExtrasExistingName() {
758 final AccountType accountType = getAccountType();
759
760 // Build "before" name
761 final ContentValues first = new ContentValues();
762 first.put(Data._ID, TEST_ID);
763 first.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
764 first.put(StructuredName.GIVEN_NAME, TEST_NAME);
765
766 // Parse extras, making sure we keep single name
767 final RawContactDelta state = getRawContact(TEST_ID, first);
768 final Bundle extras = new Bundle();
769 extras.putString(Insert.NAME, TEST_NAME2);
770 RawContactModifier.parseExtras(mContext, accountType, state, extras);
771
772 final int nameCount = state.getMimeEntriesCount(StructuredName.CONTENT_ITEM_TYPE, true);
773 assertEquals("Unexpected names", 1, nameCount);
774 }
775
776 public void testParseExtrasIgnoreLimit() {
777 final AccountType accountType = getAccountType();
778
779 // Build "before" IM
780 final ContentValues first = new ContentValues();
781 first.put(Data._ID, TEST_ID);
782 first.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
783 first.put(Im.DATA, TEST_IM);
784
785 final RawContactDelta state = getRawContact(TEST_ID, first);
786 final int beforeCount = state.getMimeEntries(Im.CONTENT_ITEM_TYPE).size();
787
788 // We should ignore data that doesn't fit account type rules, since account type
789 // only allows single Im
790 final Bundle extras = new Bundle();
791 extras.putInt(Insert.IM_PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
792 extras.putString(Insert.IM_HANDLE, TEST_IM);
793 RawContactModifier.parseExtras(mContext, accountType, state, extras);
794
795 final int afterCount = state.getMimeEntries(Im.CONTENT_ITEM_TYPE).size();
796 assertEquals("Broke account type rules", beforeCount, afterCount);
797 }
798
799 public void testParseExtrasIgnoreUnhandled() {
800 final AccountType accountType = getAccountType();
801 final RawContactDelta state = getRawContact(TEST_ID);
802
803 // We should silently ignore types unsupported by account type
804 final Bundle extras = new Bundle();
805 extras.putString(Insert.POSTAL, TEST_POSTAL);
806 RawContactModifier.parseExtras(mContext, accountType, state, extras);
807
808 assertNull("Broke accoun type rules",
809 state.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE));
810 }
811
812 public void testParseExtrasJobTitle() {
813 final AccountType accountType = getAccountType();
814 final RawContactDelta state = getRawContact(TEST_ID);
815
816 // Make sure that we create partial Organizations
817 final Bundle extras = new Bundle();
818 extras.putString(Insert.JOB_TITLE, TEST_NAME);
819 RawContactModifier.parseExtras(mContext, accountType, state, extras);
820
821 final int count = state.getMimeEntries(Organization.CONTENT_ITEM_TYPE).size();
822 assertEquals("Expected to create organization", 1, count);
823 }
824
Gary Mai698cee72016-09-19 16:09:54 -0700825 public void testMigrateNameFromGoogleToExchange() {
Yorke Lee2644d942013-10-28 11:05:43 -0700826 AccountType oldAccountType = new GoogleAccountType(getContext(), "");
827 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE);
828 DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
829
830 ContactsMockContext context = new ContactsMockContext(getContext());
831
832 RawContactDelta oldState = new RawContactDelta();
833 ContentValues mockNameValues = new ContentValues();
834 mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
835 mockNameValues.put(StructuredName.PREFIX, "prefix");
836 mockNameValues.put(StructuredName.GIVEN_NAME, "given");
837 mockNameValues.put(StructuredName.MIDDLE_NAME, "middle");
838 mockNameValues.put(StructuredName.FAMILY_NAME, "family");
839 mockNameValues.put(StructuredName.SUFFIX, "suffix");
840 mockNameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "PHONETIC_FAMILY");
841 mockNameValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "PHONETIC_MIDDLE");
842 mockNameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "PHONETIC_GIVEN");
843 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
844
845 RawContactDelta newState = new RawContactDelta();
846 RawContactModifier.migrateStructuredName(context, oldState, newState, kind);
847 List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
848 assertEquals(1, list.size());
849
850 ContentValues output = list.get(0).getAfter();
851 assertEquals("prefix", output.getAsString(StructuredName.PREFIX));
852 assertEquals("given", output.getAsString(StructuredName.GIVEN_NAME));
853 assertEquals("middle", output.getAsString(StructuredName.MIDDLE_NAME));
854 assertEquals("family", output.getAsString(StructuredName.FAMILY_NAME));
855 assertEquals("suffix", output.getAsString(StructuredName.SUFFIX));
856 // Phonetic middle name isn't supported by Exchange.
857 assertEquals("PHONETIC_FAMILY", output.getAsString(StructuredName.PHONETIC_FAMILY_NAME));
858 assertEquals("PHONETIC_GIVEN", output.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
859 }
860
Yorke Lee2644d942013-10-28 11:05:43 -0700861 public void testMigratePostalFromGoogleToExchange() {
862 AccountType oldAccountType = new GoogleAccountType(getContext(), "");
863 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE);
864 DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
865
866 RawContactDelta oldState = new RawContactDelta();
867 ContentValues mockNameValues = new ContentValues();
868 mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
869 mockNameValues.put(StructuredPostal.FORMATTED_ADDRESS, "formatted_address");
870 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
871
872 RawContactDelta newState = new RawContactDelta();
873 RawContactModifier.migratePostal(oldState, newState, kind);
874
875 List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE);
876 assertNotNull(list);
877 assertEquals(1, list.size());
878 ContentValues outputValues = list.get(0).getAfter();
879 // FORMATTED_ADDRESS isn't supported by Exchange.
880 assertNull(outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS));
881 assertEquals("formatted_address", outputValues.getAsString(StructuredPostal.STREET));
882 }
883
884 public void testMigratePostalFromExchangeToGoogle() {
885 AccountType oldAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE);
886 AccountType newAccountType = new GoogleAccountType(getContext(), "");
887 DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
888
889 RawContactDelta oldState = new RawContactDelta();
890 ContentValues mockNameValues = new ContentValues();
891 mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
892 mockNameValues.put(StructuredPostal.COUNTRY, "country");
893 mockNameValues.put(StructuredPostal.POSTCODE, "postcode");
894 mockNameValues.put(StructuredPostal.REGION, "region");
895 mockNameValues.put(StructuredPostal.CITY, "city");
896 mockNameValues.put(StructuredPostal.STREET, "street");
897 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
898
899 RawContactDelta newState = new RawContactDelta();
900 RawContactModifier.migratePostal(oldState, newState, kind);
901
902 List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE);
903 assertNotNull(list);
904 assertEquals(1, list.size());
905 ContentValues outputValues = list.get(0).getAfter();
906
907 // Check FORMATTED_ADDRESS contains all info.
908 String formattedAddress = outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
909 assertNotNull(formattedAddress);
910 assertTrue(formattedAddress.contains("country"));
911 assertTrue(formattedAddress.contains("postcode"));
912 assertTrue(formattedAddress.contains("region"));
913 assertTrue(formattedAddress.contains("postcode"));
914 assertTrue(formattedAddress.contains("city"));
915 assertTrue(formattedAddress.contains("street"));
916 }
917
918 public void testMigrateEventFromGoogleToExchange1() {
919 testMigrateEventCommon(new GoogleAccountType(getContext(), ""),
920 new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE));
921 }
922
923 public void testMigrateEventFromExchangeToGoogle() {
924 testMigrateEventCommon(new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE),
925 new GoogleAccountType(getContext(), ""));
926 }
927
928 private void testMigrateEventCommon(AccountType oldAccountType, AccountType newAccountType) {
929 DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE);
930
931 RawContactDelta oldState = new RawContactDelta();
932 ContentValues mockNameValues = new ContentValues();
933 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
934 mockNameValues.put(Event.START_DATE, "1972-02-08");
935 mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
936 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
937
938 RawContactDelta newState = new RawContactDelta();
939 RawContactModifier.migrateEvent(oldState, newState, kind, 1990);
940
941 List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE);
942 assertNotNull(list);
943 assertEquals(1, list.size()); // Anniversary should be dropped.
944 ContentValues outputValues = list.get(0).getAfter();
945
946 assertEquals("1972-02-08", outputValues.getAsString(Event.START_DATE));
947 assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue());
948 }
949
950 public void testMigrateEventFromGoogleToExchange2() {
951 AccountType oldAccountType = new GoogleAccountType(getContext(), "");
952 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE);
953 DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE);
954
955 RawContactDelta oldState = new RawContactDelta();
956 ContentValues mockNameValues = new ContentValues();
957 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
958 // No year format is not supported by Exchange.
959 mockNameValues.put(Event.START_DATE, "--06-01");
960 mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
961 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
962 mockNameValues = new ContentValues();
963 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
964 mockNameValues.put(Event.START_DATE, "1980-08-02");
965 // Anniversary is not supported by Exchange
966 mockNameValues.put(Event.TYPE, Event.TYPE_ANNIVERSARY);
967 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
968
969 RawContactDelta newState = new RawContactDelta();
970 RawContactModifier.migrateEvent(oldState, newState, kind, 1990);
971
972 List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE);
973 assertNotNull(list);
974 assertEquals(1, list.size()); // Anniversary should be dropped.
975 ContentValues outputValues = list.get(0).getAfter();
976
977 // Default year should be used.
978 assertEquals("1990-06-01", outputValues.getAsString(Event.START_DATE));
979 assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue());
980 }
981
982 public void testMigrateEmailFromGoogleToExchange() {
983 AccountType oldAccountType = new GoogleAccountType(getContext(), "");
984 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE);
985 DataKind kind = newAccountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
986
987 RawContactDelta oldState = new RawContactDelta();
988 ContentValues mockNameValues = new ContentValues();
989 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
990 mockNameValues.put(Email.TYPE, Email.TYPE_CUSTOM);
991 mockNameValues.put(Email.LABEL, "custom_type");
992 mockNameValues.put(Email.ADDRESS, "address1");
993 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
994 mockNameValues = new ContentValues();
995 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
996 mockNameValues.put(Email.TYPE, Email.TYPE_HOME);
997 mockNameValues.put(Email.ADDRESS, "address2");
998 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
999 mockNameValues = new ContentValues();
1000 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1001 mockNameValues.put(Email.TYPE, Email.TYPE_WORK);
1002 mockNameValues.put(Email.ADDRESS, "address3");
1003 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1004 // Exchange can have up to 3 email entries. This 4th entry should be dropped.
1005 mockNameValues = new ContentValues();
1006 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1007 mockNameValues.put(Email.TYPE, Email.TYPE_OTHER);
1008 mockNameValues.put(Email.ADDRESS, "address4");
1009 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1010
1011 RawContactDelta newState = new RawContactDelta();
1012 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
1013
1014 List<ValuesDelta> list = newState.getMimeEntries(Email.CONTENT_ITEM_TYPE);
1015 assertNotNull(list);
1016 assertEquals(3, list.size());
1017
1018 ContentValues outputValues = list.get(0).getAfter();
1019 assertEquals(Email.TYPE_CUSTOM, outputValues.getAsInteger(Email.TYPE).intValue());
1020 assertEquals("custom_type", outputValues.getAsString(Email.LABEL));
1021 assertEquals("address1", outputValues.getAsString(Email.ADDRESS));
1022
1023 outputValues = list.get(1).getAfter();
1024 assertEquals(Email.TYPE_HOME, outputValues.getAsInteger(Email.TYPE).intValue());
1025 assertEquals("address2", outputValues.getAsString(Email.ADDRESS));
1026
1027 outputValues = list.get(2).getAfter();
1028 assertEquals(Email.TYPE_WORK, outputValues.getAsInteger(Email.TYPE).intValue());
1029 assertEquals("address3", outputValues.getAsString(Email.ADDRESS));
1030 }
1031
1032 public void testMigrateImFromGoogleToExchange() {
1033 AccountType oldAccountType = new GoogleAccountType(getContext(), "");
1034 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE);
1035 DataKind kind = newAccountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
1036
1037 RawContactDelta oldState = new RawContactDelta();
1038 ContentValues mockNameValues = new ContentValues();
1039 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
1040 // Exchange doesn't support TYPE_HOME
1041 mockNameValues.put(Im.TYPE, Im.TYPE_HOME);
1042 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_JABBER);
1043 mockNameValues.put(Im.DATA, "im1");
1044 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1045
1046 mockNameValues = new ContentValues();
1047 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
1048 // Exchange doesn't support TYPE_WORK
1049 mockNameValues.put(Im.TYPE, Im.TYPE_WORK);
1050 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_YAHOO);
1051 mockNameValues.put(Im.DATA, "im2");
1052 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1053
1054 mockNameValues = new ContentValues();
1055 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
1056 mockNameValues.put(Im.TYPE, Im.TYPE_OTHER);
1057 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
1058 mockNameValues.put(Im.CUSTOM_PROTOCOL, "custom_protocol");
1059 mockNameValues.put(Im.DATA, "im3");
1060 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1061
1062 // Exchange can have up to 3 IM entries. This 4th entry should be dropped.
1063 mockNameValues = new ContentValues();
1064 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
1065 mockNameValues.put(Im.TYPE, Im.TYPE_OTHER);
1066 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
1067 mockNameValues.put(Im.DATA, "im4");
1068 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1069
1070 RawContactDelta newState = new RawContactDelta();
1071 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
1072
1073 List<ValuesDelta> list = newState.getMimeEntries(Im.CONTENT_ITEM_TYPE);
1074 assertNotNull(list);
1075 assertEquals(3, list.size());
1076
1077 assertNotNull(kind.defaultValues.getAsInteger(Im.TYPE));
1078
1079 int defaultType = kind.defaultValues.getAsInteger(Im.TYPE);
1080
1081 ContentValues outputValues = list.get(0).getAfter();
1082 // HOME should become default type.
1083 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
1084 assertEquals(Im.PROTOCOL_JABBER, outputValues.getAsInteger(Im.PROTOCOL).intValue());
1085 assertEquals("im1", outputValues.getAsString(Im.DATA));
1086
1087 outputValues = list.get(1).getAfter();
1088 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
1089 assertEquals(Im.PROTOCOL_YAHOO, outputValues.getAsInteger(Im.PROTOCOL).intValue());
1090 assertEquals("im2", outputValues.getAsString(Im.DATA));
1091
1092 outputValues = list.get(2).getAfter();
1093 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
1094 assertEquals(Im.PROTOCOL_CUSTOM, outputValues.getAsInteger(Im.PROTOCOL).intValue());
1095 assertEquals("custom_protocol", outputValues.getAsString(Im.CUSTOM_PROTOCOL));
1096 assertEquals("im3", outputValues.getAsString(Im.DATA));
1097 }
1098
1099 public void testMigratePhoneFromGoogleToExchange() {
1100 AccountType oldAccountType = new GoogleAccountType(getContext(), "");
1101 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE);
1102 DataKind kind = newAccountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
1103
1104 // Create 5 numbers.
1105 // - "1" -- HOME
1106 // - "2" -- WORK
1107 // - "3" -- CUSTOM
1108 // - "4" -- WORK
1109 // - "5" -- WORK_MOBILE
1110 // Then we convert it to Exchange account type.
1111 // - "1" -- HOME
1112 // - "2" -- WORK
1113 // - "3" -- Because CUSTOM is not supported, it'll be changed to the default, MOBILE
1114 // - "4" -- WORK
1115 // - "5" -- WORK_MOBILE not suppoted again, so will be MOBILE.
1116 // But then, Exchange doesn't support multiple MOBILE numbers, so "5" will be removed.
1117 // i.e. the result will be:
1118 // - "1" -- HOME
1119 // - "2" -- WORK
1120 // - "3" -- MOBILE
1121 // - "4" -- WORK
1122
1123 RawContactDelta oldState = new RawContactDelta();
1124 ContentValues mockNameValues = new ContentValues();
1125 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1126 mockNameValues.put(Phone.TYPE, Phone.TYPE_HOME);
1127 mockNameValues.put(Phone.NUMBER, "1");
1128 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1129 mockNameValues = new ContentValues();
1130 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1131 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK);
1132 mockNameValues.put(Phone.NUMBER, "2");
1133 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1134 mockNameValues = new ContentValues();
1135 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1136 // Exchange doesn't support this type. Default to MOBILE
1137 mockNameValues.put(Phone.TYPE, Phone.TYPE_CUSTOM);
1138 mockNameValues.put(Phone.LABEL, "custom_type");
1139 mockNameValues.put(Phone.NUMBER, "3");
1140 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1141 mockNameValues = new ContentValues();
1142 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1143 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK);
1144 mockNameValues.put(Phone.NUMBER, "4");
1145 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1146 mockNameValues = new ContentValues();
1147
1148 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1149 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK_MOBILE);
1150 mockNameValues.put(Phone.NUMBER, "5");
1151 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1152
1153 RawContactDelta newState = new RawContactDelta();
1154 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
1155
1156 List<ValuesDelta> list = newState.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
1157 assertNotNull(list);
1158 assertEquals(4, list.size());
1159
1160 int defaultType = Phone.TYPE_MOBILE;
1161
1162 ContentValues outputValues = list.get(0).getAfter();
1163 assertEquals(Phone.TYPE_HOME, outputValues.getAsInteger(Phone.TYPE).intValue());
1164 assertEquals("1", outputValues.getAsString(Phone.NUMBER));
1165 outputValues = list.get(1).getAfter();
1166 assertEquals(Phone.TYPE_WORK, outputValues.getAsInteger(Phone.TYPE).intValue());
1167 assertEquals("2", outputValues.getAsString(Phone.NUMBER));
1168 outputValues = list.get(2).getAfter();
1169 assertEquals(defaultType, outputValues.getAsInteger(Phone.TYPE).intValue());
1170 assertNull(outputValues.getAsInteger(Phone.LABEL));
1171 assertEquals("3", outputValues.getAsString(Phone.NUMBER));
1172 outputValues = list.get(3).getAfter();
1173 assertEquals(Phone.TYPE_WORK, outputValues.getAsInteger(Phone.TYPE).intValue());
1174 assertEquals("4", outputValues.getAsString(Phone.NUMBER));
1175 }
1176
1177 public void testMigrateOrganizationFromGoogleToExchange() {
1178 AccountType oldAccountType = new GoogleAccountType(getContext(), "");
1179 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE);
1180 DataKind kind = newAccountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
1181
1182 RawContactDelta oldState = new RawContactDelta();
1183 ContentValues mockNameValues = new ContentValues();
1184 mockNameValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
1185 mockNameValues.put(Organization.COMPANY, "company1");
1186 mockNameValues.put(Organization.DEPARTMENT, "department1");
1187 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
1188
1189 RawContactDelta newState = new RawContactDelta();
1190 RawContactModifier.migrateGenericWithoutTypeColumn(oldState, newState, kind);
1191
1192 List<ValuesDelta> list = newState.getMimeEntries(Organization.CONTENT_ITEM_TYPE);
1193 assertNotNull(list);
1194 assertEquals(1, list.size());
1195
1196 ContentValues outputValues = list.get(0).getAfter();
1197 assertEquals("company1", outputValues.getAsString(Organization.COMPANY));
1198 assertEquals("department1", outputValues.getAsString(Organization.DEPARTMENT));
1199 }
1200}