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