1 /* 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 17 package com.android.contacts.model; 18 19 import android.content.ContentProviderOperation; 20 import android.content.ContentValues; 21 import android.os.Bundle; 22 import android.provider.ContactsContract.CommonDataKinds.Email; 23 import android.provider.ContactsContract.CommonDataKinds.Event; 24 import android.provider.ContactsContract.CommonDataKinds.Im; 25 import android.provider.ContactsContract.CommonDataKinds.Organization; 26 import android.provider.ContactsContract.CommonDataKinds.Phone; 27 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 28 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 29 import android.provider.ContactsContract.Data; 30 import android.provider.ContactsContract.Intents.Insert; 31 import android.provider.ContactsContract.RawContacts; 32 import android.test.AndroidTestCase; 33 import android.test.suitebuilder.annotation.LargeTest; 34 35 import com.android.contacts.R; 36 import com.android.contacts.compat.CompatUtils; 37 import com.android.contacts.model.account.AccountType; 38 import com.android.contacts.model.account.AccountType.EditType; 39 import com.android.contacts.model.account.ExchangeAccountType; 40 import com.android.contacts.model.account.GoogleAccountType; 41 import com.android.contacts.model.dataitem.DataKind; 42 import com.android.contacts.test.mocks.ContactsMockContext; 43 import com.android.contacts.test.mocks.MockAccountTypeManager; 44 45 import com.google.common.collect.Lists; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 50 /** 51 * Tests for {@link RawContactModifier} to verify that {@link AccountType} 52 * constraints are being enforced correctly. 53 */ 54 @LargeTest 55 public class RawContactModifierTests extends AndroidTestCase { 56 public static final String TAG = "EntityModifierTests"; 57 58 // From android.content.ContentProviderOperation 59 public static final int TYPE_INSERT = 1; 60 61 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 setUp()76 public void setUp() { 77 mContext = getContext(); 78 } 79 80 public static class MockContactsSource extends AccountType { 81 MockContactsSource()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 isGroupMembershipEditable()137 public boolean isGroupMembershipEditable() { 138 return false; 139 } 140 141 @Override areContactsWritable()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 */ getAccountType()151 protected AccountType getAccountType() { 152 return new MockContactsSource(); 153 } 154 155 /** 156 * Build {@link AccountTypeManager} instance. 157 */ getAccountTypes(AccountType... types)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 */ getRawContact(Long existingId, ContentValues... entries)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 */ assertContains(List<?> list, Object object)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 */ assertNotContains(List<?> list, Object object)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 */ testValidTypes()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 214 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true); 215 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 223 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true); 224 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 233 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true); 234 assertNotContains(validTypes, typeHome); 235 assertNotContains(validTypes, typeWork); 236 assertNotContains(validTypes, typeOther); 237 } 238 239 /** 240 * Test which valid types there are when trying to update the editor type. 241 * {@link RawContactModifier#getValidTypes(RawContactDelta, DataKind, EditType, Boolean)} 242 */ testValidTypesWhenUpdating()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 /** 277 * Test {@link RawContactModifier#canInsert(RawContactDelta, DataKind)} by 278 * inserting various rows. 279 */ testCanInsert()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 */ testBestValidType()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 testIsEmptyEmpty()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 testIsEmptyDirectFields()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 testTrimEmptySingle()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 384 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 385 state.buildDiffWrapper(diff); 386 assertEquals("Unexpected operations", 3, diff.size()); 387 { 388 final CPOWrapper cpoWrapper = diff.get(0); 389 final ContentProviderOperation oper = cpoWrapper.getOperation(); 390 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 391 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 392 } 393 { 394 final CPOWrapper cpoWrapper = diff.get(1); 395 final ContentProviderOperation oper = cpoWrapper.getOperation(); 396 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper)); 397 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 398 } 399 { 400 final CPOWrapper cpoWrapper = diff.get(2); 401 final ContentProviderOperation oper = cpoWrapper.getOperation(); 402 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 403 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(); 409 state.buildDiffWrapper(diff); 410 assertEquals("Unexpected operations", 1, diff.size()); 411 { 412 final CPOWrapper cpoWrapper = diff.get(0); 413 final ContentProviderOperation oper = cpoWrapper.getOperation(); 414 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper)); 415 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 416 } 417 } 418 testTrimEmptySpaces()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(), 434 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, 435 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 testTrimLeaveValid()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(), 460 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, 461 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(), 469 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, 470 RawContactDeltaListTests.buildDataInsert(values, TEST_ID)), 471 RawContactDeltaListTests.buildUpdateAggregationDefault()); 472 } 473 testTrimEmptyUntouched()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 487 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 488 state.buildDiffWrapper(diff); 489 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(); 494 state.buildDiffWrapper(diff); 495 assertEquals("Unexpected operations", 0, diff.size()); 496 } 497 testTrimEmptyAfterUpdate()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 512 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 513 state.buildDiffWrapper(diff); 514 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(); 520 state.buildDiffWrapper(diff); 521 assertEquals("Unexpected operations", 3, diff.size()); 522 { 523 final CPOWrapper cpoWrapper = diff.get(0); 524 final ContentProviderOperation oper = cpoWrapper.getOperation(); 525 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 526 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 527 } 528 { 529 final CPOWrapper cpoWrapper = diff.get(1); 530 final ContentProviderOperation oper = cpoWrapper.getOperation(); 531 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper)); 532 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 533 } 534 { 535 final CPOWrapper cpoWrapper = diff.get(2); 536 final ContentProviderOperation oper = cpoWrapper.getOperation(); 537 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 538 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(); 544 state.buildDiffWrapper(diff); 545 assertEquals("Unexpected operations", 1, diff.size()); 546 { 547 final CPOWrapper cpoWrapper = diff.get(0); 548 final ContentProviderOperation oper = cpoWrapper.getOperation(); 549 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper)); 550 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 551 } 552 } 553 testTrimInsertEmpty()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 565 // Build diff, expecting single insert 566 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 567 state.buildDiffWrapper(diff); 568 assertEquals("Unexpected operations", 2, diff.size()); 569 { 570 final CPOWrapper cpoWrapper = diff.get(0); 571 final ContentProviderOperation oper = cpoWrapper.getOperation(); 572 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper)); 573 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(); 579 state.buildDiffWrapper(diff); 580 assertEquals("Unexpected operations", 0, diff.size()); 581 } 582 testTrimInsertInsert()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 596 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 597 state.buildDiffWrapper(diff); 598 assertEquals("Unexpected operations", 3, diff.size()); 599 { 600 final CPOWrapper cpoWrapper = diff.get(0); 601 final ContentProviderOperation oper = cpoWrapper.getOperation(); 602 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper)); 603 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 604 } 605 { 606 final CPOWrapper cpoWrapper = diff.get(1); 607 final ContentProviderOperation oper = cpoWrapper.getOperation(); 608 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper)); 609 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(); 615 state.buildDiffWrapper(diff); 616 assertEquals("Unexpected operations", 0, diff.size()); 617 } 618 testTrimUpdateRemain()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 643 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 644 state.buildDiffWrapper(diff); 645 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(); 651 state.buildDiffWrapper(diff); 652 assertEquals("Unexpected operations", 3, diff.size()); 653 { 654 final CPOWrapper cpoWrapper = diff.get(0); 655 final ContentProviderOperation oper = cpoWrapper.getOperation(); 656 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 657 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 658 } 659 { 660 final CPOWrapper cpoWrapper = diff.get(1); 661 final ContentProviderOperation oper = cpoWrapper.getOperation(); 662 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper)); 663 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 664 } 665 { 666 final CPOWrapper cpoWrapper = diff.get(2); 667 final ContentProviderOperation oper = cpoWrapper.getOperation(); 668 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 669 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(); 675 state.buildDiffWrapper(diff); 676 assertEquals("Unexpected operations", 3, diff.size()); 677 { 678 final CPOWrapper cpoWrapper = diff.get(0); 679 final ContentProviderOperation oper = cpoWrapper.getOperation(); 680 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 681 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 682 } 683 { 684 final CPOWrapper cpoWrapper = diff.get(1); 685 final ContentProviderOperation oper = cpoWrapper.getOperation(); 686 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper)); 687 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 688 } 689 { 690 final CPOWrapper cpoWrapper = diff.get(2); 691 final ContentProviderOperation oper = cpoWrapper.getOperation(); 692 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 693 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 694 } 695 } 696 testTrimUpdateUpdate()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 715 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 716 state.buildDiffWrapper(diff); 717 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(); 723 state.buildDiffWrapper(diff); 724 assertEquals("Unexpected operations", 3, diff.size()); 725 { 726 final CPOWrapper cpoWrapper = diff.get(0); 727 final ContentProviderOperation oper = cpoWrapper.getOperation(); 728 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 729 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 730 } 731 { 732 final CPOWrapper cpoWrapper = diff.get(1); 733 final ContentProviderOperation oper = cpoWrapper.getOperation(); 734 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper)); 735 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 736 } 737 { 738 final CPOWrapper cpoWrapper = diff.get(2); 739 final ContentProviderOperation oper = cpoWrapper.getOperation(); 740 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 741 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(); 747 state.buildDiffWrapper(diff); 748 assertEquals("Unexpected operations", 1, diff.size()); 749 { 750 final CPOWrapper cpoWrapper = diff.get(0); 751 final ContentProviderOperation oper = cpoWrapper.getOperation(); 752 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper)); 753 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 754 } 755 } 756 testParseExtrasExistingName()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 testParseExtrasIgnoreLimit()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 testParseExtrasIgnoreUnhandled()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 testParseExtrasJobTitle()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 testMigrateNameFromGoogleToExchange()825 public void testMigrateNameFromGoogleToExchange() { 826 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 testMigratePostalFromGoogleToExchange()861 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 testMigratePostalFromExchangeToGoogle()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 testMigrateEventFromGoogleToExchange1()918 public void testMigrateEventFromGoogleToExchange1() { 919 testMigrateEventCommon(new GoogleAccountType(getContext(), ""), 920 new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE)); 921 } 922 testMigrateEventFromExchangeToGoogle()923 public void testMigrateEventFromExchangeToGoogle() { 924 testMigrateEventCommon(new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE), 925 new GoogleAccountType(getContext(), "")); 926 } 927 testMigrateEventCommon(AccountType oldAccountType, AccountType newAccountType)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 testMigrateEventFromGoogleToExchange2()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 testMigrateEmailFromGoogleToExchange()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 testMigrateImFromGoogleToExchange()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 testMigratePhoneFromGoogleToExchange()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 testMigrateOrganizationFromGoogleToExchange()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 } 1201