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 17package com.android.contacts.common; 18 19import 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 37import com.android.contacts.common.compat.CompatUtils; 38import com.android.contacts.common.model.AccountTypeManager; 39import com.android.contacts.common.model.CPOWrapper; 40import 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 66 // From android.content.ContentProviderOperation 67 public static final int TYPE_INSERT = 1; 68 69 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 222 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true); 223 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 231 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true); 232 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 241 validTypes = RawContactModifier.getValidTypes(state, kindPhone, null, true, null, true); 242 assertNotContains(validTypes, typeHome); 243 assertNotContains(validTypes, typeWork); 244 assertNotContains(validTypes, typeOther); 245 } 246 247 /** 248 * 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 /** 285 * 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 392 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 393 state.buildDiffWrapper(diff); 394 assertEquals("Unexpected operations", 3, diff.size()); 395 { 396 final CPOWrapper cpoWrapper = diff.get(0); 397 final ContentProviderOperation oper = cpoWrapper.getOperation(); 398 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 399 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 400 } 401 { 402 final CPOWrapper cpoWrapper = diff.get(1); 403 final ContentProviderOperation oper = cpoWrapper.getOperation(); 404 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper)); 405 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 406 } 407 { 408 final CPOWrapper cpoWrapper = diff.get(2); 409 final ContentProviderOperation oper = cpoWrapper.getOperation(); 410 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 411 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(); 417 state.buildDiffWrapper(diff); 418 assertEquals("Unexpected operations", 1, diff.size()); 419 { 420 final CPOWrapper cpoWrapper = diff.get(0); 421 final ContentProviderOperation oper = cpoWrapper.getOperation(); 422 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper)); 423 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(), 442 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, 443 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(), 468 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, 469 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(), 477 RawContactDeltaListTests.buildCPOWrapper(Data.CONTENT_URI, TYPE_INSERT, 478 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 495 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 496 state.buildDiffWrapper(diff); 497 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(); 502 state.buildDiffWrapper(diff); 503 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 520 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 521 state.buildDiffWrapper(diff); 522 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(); 528 state.buildDiffWrapper(diff); 529 assertEquals("Unexpected operations", 3, diff.size()); 530 { 531 final CPOWrapper cpoWrapper = diff.get(0); 532 final ContentProviderOperation oper = cpoWrapper.getOperation(); 533 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 534 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 535 } 536 { 537 final CPOWrapper cpoWrapper = diff.get(1); 538 final ContentProviderOperation oper = cpoWrapper.getOperation(); 539 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper)); 540 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 541 } 542 { 543 final CPOWrapper cpoWrapper = diff.get(2); 544 final ContentProviderOperation oper = cpoWrapper.getOperation(); 545 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 546 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(); 552 state.buildDiffWrapper(diff); 553 assertEquals("Unexpected operations", 1, diff.size()); 554 { 555 final CPOWrapper cpoWrapper = diff.get(0); 556 final ContentProviderOperation oper = cpoWrapper.getOperation(); 557 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper)); 558 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 573 // Build diff, expecting single insert 574 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 575 state.buildDiffWrapper(diff); 576 assertEquals("Unexpected operations", 2, diff.size()); 577 { 578 final CPOWrapper cpoWrapper = diff.get(0); 579 final ContentProviderOperation oper = cpoWrapper.getOperation(); 580 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper)); 581 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(); 587 state.buildDiffWrapper(diff); 588 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 604 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 605 state.buildDiffWrapper(diff); 606 assertEquals("Unexpected operations", 3, diff.size()); 607 { 608 final CPOWrapper cpoWrapper = diff.get(0); 609 final ContentProviderOperation oper = cpoWrapper.getOperation(); 610 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper)); 611 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 612 } 613 { 614 final CPOWrapper cpoWrapper = diff.get(1); 615 final ContentProviderOperation oper = cpoWrapper.getOperation(); 616 assertTrue("Incorrect type", CompatUtils.isInsertCompat(cpoWrapper)); 617 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(); 623 state.buildDiffWrapper(diff); 624 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 651 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 652 state.buildDiffWrapper(diff); 653 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(); 659 state.buildDiffWrapper(diff); 660 assertEquals("Unexpected operations", 3, diff.size()); 661 { 662 final CPOWrapper cpoWrapper = diff.get(0); 663 final ContentProviderOperation oper = cpoWrapper.getOperation(); 664 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 665 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 666 } 667 { 668 final CPOWrapper cpoWrapper = diff.get(1); 669 final ContentProviderOperation oper = cpoWrapper.getOperation(); 670 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper)); 671 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 672 } 673 { 674 final CPOWrapper cpoWrapper = diff.get(2); 675 final ContentProviderOperation oper = cpoWrapper.getOperation(); 676 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 677 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(); 683 state.buildDiffWrapper(diff); 684 assertEquals("Unexpected operations", 3, diff.size()); 685 { 686 final CPOWrapper cpoWrapper = diff.get(0); 687 final ContentProviderOperation oper = cpoWrapper.getOperation(); 688 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 689 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 690 } 691 { 692 final CPOWrapper cpoWrapper = diff.get(1); 693 final ContentProviderOperation oper = cpoWrapper.getOperation(); 694 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper)); 695 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 696 } 697 { 698 final CPOWrapper cpoWrapper = diff.get(2); 699 final ContentProviderOperation oper = cpoWrapper.getOperation(); 700 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 701 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 723 final ArrayList<CPOWrapper> diff = Lists.newArrayList(); 724 state.buildDiffWrapper(diff); 725 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(); 731 state.buildDiffWrapper(diff); 732 assertEquals("Unexpected operations", 3, diff.size()); 733 { 734 final CPOWrapper cpoWrapper = diff.get(0); 735 final ContentProviderOperation oper = cpoWrapper.getOperation(); 736 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 737 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 738 } 739 { 740 final CPOWrapper cpoWrapper = diff.get(1); 741 final ContentProviderOperation oper = cpoWrapper.getOperation(); 742 assertTrue("Incorrect type", CompatUtils.isUpdateCompat(cpoWrapper)); 743 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 744 } 745 { 746 final CPOWrapper cpoWrapper = diff.get(2); 747 final ContentProviderOperation oper = cpoWrapper.getOperation(); 748 assertTrue("Expected aggregation mode change", CompatUtils.isUpdateCompat(cpoWrapper)); 749 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(); 755 state.buildDiffWrapper(diff); 756 assertEquals("Unexpected operations", 1, diff.size()); 757 { 758 final CPOWrapper cpoWrapper = diff.get(0); 759 final ContentProviderOperation oper = cpoWrapper.getOperation(); 760 assertTrue("Incorrect type", CompatUtils.isDeleteCompat(cpoWrapper)); 761 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 833 public void testMigrateWithDisplayNameFromGoogleToExchange1() { 834 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 869 public void testMigrateWithDisplayNameFromGoogleToExchange2() { 870 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 871 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 872 DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE); 873 874 ContactsMockContext context = new ContactsMockContext(getContext()); 875 MockContentProvider provider = context.getContactsProvider(); 876 877 String inputDisplayName = "prefix given middle family suffix"; 878 // The method will ask the provider to split/join StructuredName. 879 Uri uriForBuildDisplayName = 880 ContactsContract.AUTHORITY_URI 881 .buildUpon() 882 .appendPath("complete_name") 883 .appendQueryParameter(StructuredName.DISPLAY_NAME, inputDisplayName) 884 .build(); 885 provider.expectQuery(uriForBuildDisplayName) 886 .returnRow("prefix", "given", "middle", "family", "suffix") 887 .withProjection(StructuredName.PREFIX, StructuredName.GIVEN_NAME, 888 StructuredName.MIDDLE_NAME, StructuredName.FAMILY_NAME, 889 StructuredName.SUFFIX); 890 891 RawContactDelta oldState = new RawContactDelta(); 892 ContentValues mockNameValues = new ContentValues(); 893 mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 894 mockNameValues.put(StructuredName.DISPLAY_NAME, inputDisplayName); 895 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 896 897 RawContactDelta newState = new RawContactDelta(); 898 RawContactModifier.migrateStructuredName(context, oldState, newState, kind); 899 List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE); 900 assertEquals(1, list.size()); 901 902 ContentValues outputValues = list.get(0).getAfter(); 903 assertEquals("prefix", outputValues.getAsString(StructuredName.PREFIX)); 904 assertEquals("given", outputValues.getAsString(StructuredName.GIVEN_NAME)); 905 assertEquals("middle", outputValues.getAsString(StructuredName.MIDDLE_NAME)); 906 assertEquals("family", outputValues.getAsString(StructuredName.FAMILY_NAME)); 907 assertEquals("suffix", outputValues.getAsString(StructuredName.SUFFIX)); 908 } 909 910 public void testMigrateWithStructuredNameFromExchangeToGoogle() { 911 AccountType oldAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 912 AccountType newAccountType = new GoogleAccountType(getContext(), ""); 913 DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE); 914 915 ContactsMockContext context = new ContactsMockContext(getContext()); 916 MockContentProvider provider = context.getContactsProvider(); 917 918 // The method will ask the provider to split/join StructuredName. 919 Uri uriForBuildDisplayName = 920 ContactsContract.AUTHORITY_URI 921 .buildUpon() 922 .appendPath("complete_name") 923 .appendQueryParameter(StructuredName.PREFIX, "prefix") 924 .appendQueryParameter(StructuredName.GIVEN_NAME, "given") 925 .appendQueryParameter(StructuredName.MIDDLE_NAME, "middle") 926 .appendQueryParameter(StructuredName.FAMILY_NAME, "family") 927 .appendQueryParameter(StructuredName.SUFFIX, "suffix") 928 .build(); 929 provider.expectQuery(uriForBuildDisplayName) 930 .returnRow("prefix given middle family suffix") 931 .withProjection(StructuredName.DISPLAY_NAME); 932 933 RawContactDelta oldState = new RawContactDelta(); 934 ContentValues mockNameValues = new ContentValues(); 935 mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 936 mockNameValues.put(StructuredName.PREFIX, "prefix"); 937 mockNameValues.put(StructuredName.GIVEN_NAME, "given"); 938 mockNameValues.put(StructuredName.MIDDLE_NAME, "middle"); 939 mockNameValues.put(StructuredName.FAMILY_NAME, "family"); 940 mockNameValues.put(StructuredName.SUFFIX, "suffix"); 941 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 942 943 RawContactDelta newState = new RawContactDelta(); 944 RawContactModifier.migrateStructuredName(context, oldState, newState, kind); 945 946 List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE); 947 assertNotNull(list); 948 assertEquals(1, list.size()); 949 ContentValues outputValues = list.get(0).getAfter(); 950 assertEquals("prefix given middle family suffix", 951 outputValues.getAsString(StructuredName.DISPLAY_NAME)); 952 } 953 954 public void testMigratePostalFromGoogleToExchange() { 955 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 956 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 957 DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE); 958 959 RawContactDelta oldState = new RawContactDelta(); 960 ContentValues mockNameValues = new ContentValues(); 961 mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 962 mockNameValues.put(StructuredPostal.FORMATTED_ADDRESS, "formatted_address"); 963 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 964 965 RawContactDelta newState = new RawContactDelta(); 966 RawContactModifier.migratePostal(oldState, newState, kind); 967 968 List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE); 969 assertNotNull(list); 970 assertEquals(1, list.size()); 971 ContentValues outputValues = list.get(0).getAfter(); 972 // FORMATTED_ADDRESS isn't supported by Exchange. 973 assertNull(outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS)); 974 assertEquals("formatted_address", outputValues.getAsString(StructuredPostal.STREET)); 975 } 976 977 public void testMigratePostalFromExchangeToGoogle() { 978 AccountType oldAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 979 AccountType newAccountType = new GoogleAccountType(getContext(), ""); 980 DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE); 981 982 RawContactDelta oldState = new RawContactDelta(); 983 ContentValues mockNameValues = new ContentValues(); 984 mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 985 mockNameValues.put(StructuredPostal.COUNTRY, "country"); 986 mockNameValues.put(StructuredPostal.POSTCODE, "postcode"); 987 mockNameValues.put(StructuredPostal.REGION, "region"); 988 mockNameValues.put(StructuredPostal.CITY, "city"); 989 mockNameValues.put(StructuredPostal.STREET, "street"); 990 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 991 992 RawContactDelta newState = new RawContactDelta(); 993 RawContactModifier.migratePostal(oldState, newState, kind); 994 995 List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE); 996 assertNotNull(list); 997 assertEquals(1, list.size()); 998 ContentValues outputValues = list.get(0).getAfter(); 999 1000 // Check FORMATTED_ADDRESS contains all info. 1001 String formattedAddress = outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS); 1002 assertNotNull(formattedAddress); 1003 assertTrue(formattedAddress.contains("country")); 1004 assertTrue(formattedAddress.contains("postcode")); 1005 assertTrue(formattedAddress.contains("region")); 1006 assertTrue(formattedAddress.contains("postcode")); 1007 assertTrue(formattedAddress.contains("city")); 1008 assertTrue(formattedAddress.contains("street")); 1009 } 1010 1011 public void testMigrateEventFromGoogleToExchange1() { 1012 testMigrateEventCommon(new GoogleAccountType(getContext(), ""), 1013 new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE)); 1014 } 1015 1016 public void testMigrateEventFromExchangeToGoogle() { 1017 testMigrateEventCommon(new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE), 1018 new GoogleAccountType(getContext(), "")); 1019 } 1020 1021 private void testMigrateEventCommon(AccountType oldAccountType, AccountType newAccountType) { 1022 DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE); 1023 1024 RawContactDelta oldState = new RawContactDelta(); 1025 ContentValues mockNameValues = new ContentValues(); 1026 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); 1027 mockNameValues.put(Event.START_DATE, "1972-02-08"); 1028 mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY); 1029 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1030 1031 RawContactDelta newState = new RawContactDelta(); 1032 RawContactModifier.migrateEvent(oldState, newState, kind, 1990); 1033 1034 List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE); 1035 assertNotNull(list); 1036 assertEquals(1, list.size()); // Anniversary should be dropped. 1037 ContentValues outputValues = list.get(0).getAfter(); 1038 1039 assertEquals("1972-02-08", outputValues.getAsString(Event.START_DATE)); 1040 assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue()); 1041 } 1042 1043 public void testMigrateEventFromGoogleToExchange2() { 1044 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1045 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 1046 DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE); 1047 1048 RawContactDelta oldState = new RawContactDelta(); 1049 ContentValues mockNameValues = new ContentValues(); 1050 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); 1051 // No year format is not supported by Exchange. 1052 mockNameValues.put(Event.START_DATE, "--06-01"); 1053 mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY); 1054 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1055 mockNameValues = new ContentValues(); 1056 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); 1057 mockNameValues.put(Event.START_DATE, "1980-08-02"); 1058 // Anniversary is not supported by Exchange 1059 mockNameValues.put(Event.TYPE, Event.TYPE_ANNIVERSARY); 1060 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1061 1062 RawContactDelta newState = new RawContactDelta(); 1063 RawContactModifier.migrateEvent(oldState, newState, kind, 1990); 1064 1065 List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE); 1066 assertNotNull(list); 1067 assertEquals(1, list.size()); // Anniversary should be dropped. 1068 ContentValues outputValues = list.get(0).getAfter(); 1069 1070 // Default year should be used. 1071 assertEquals("1990-06-01", outputValues.getAsString(Event.START_DATE)); 1072 assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue()); 1073 } 1074 1075 public void testMigrateEmailFromGoogleToExchange() { 1076 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1077 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 1078 DataKind kind = newAccountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE); 1079 1080 RawContactDelta oldState = new RawContactDelta(); 1081 ContentValues mockNameValues = new ContentValues(); 1082 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1083 mockNameValues.put(Email.TYPE, Email.TYPE_CUSTOM); 1084 mockNameValues.put(Email.LABEL, "custom_type"); 1085 mockNameValues.put(Email.ADDRESS, "address1"); 1086 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1087 mockNameValues = new ContentValues(); 1088 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1089 mockNameValues.put(Email.TYPE, Email.TYPE_HOME); 1090 mockNameValues.put(Email.ADDRESS, "address2"); 1091 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1092 mockNameValues = new ContentValues(); 1093 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1094 mockNameValues.put(Email.TYPE, Email.TYPE_WORK); 1095 mockNameValues.put(Email.ADDRESS, "address3"); 1096 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1097 // Exchange can have up to 3 email entries. This 4th entry should be dropped. 1098 mockNameValues = new ContentValues(); 1099 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1100 mockNameValues.put(Email.TYPE, Email.TYPE_OTHER); 1101 mockNameValues.put(Email.ADDRESS, "address4"); 1102 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1103 1104 RawContactDelta newState = new RawContactDelta(); 1105 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind); 1106 1107 List<ValuesDelta> list = newState.getMimeEntries(Email.CONTENT_ITEM_TYPE); 1108 assertNotNull(list); 1109 assertEquals(3, list.size()); 1110 1111 ContentValues outputValues = list.get(0).getAfter(); 1112 assertEquals(Email.TYPE_CUSTOM, outputValues.getAsInteger(Email.TYPE).intValue()); 1113 assertEquals("custom_type", outputValues.getAsString(Email.LABEL)); 1114 assertEquals("address1", outputValues.getAsString(Email.ADDRESS)); 1115 1116 outputValues = list.get(1).getAfter(); 1117 assertEquals(Email.TYPE_HOME, outputValues.getAsInteger(Email.TYPE).intValue()); 1118 assertEquals("address2", outputValues.getAsString(Email.ADDRESS)); 1119 1120 outputValues = list.get(2).getAfter(); 1121 assertEquals(Email.TYPE_WORK, outputValues.getAsInteger(Email.TYPE).intValue()); 1122 assertEquals("address3", outputValues.getAsString(Email.ADDRESS)); 1123 } 1124 1125 public void testMigrateImFromGoogleToExchange() { 1126 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1127 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 1128 DataKind kind = newAccountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE); 1129 1130 RawContactDelta oldState = new RawContactDelta(); 1131 ContentValues mockNameValues = new ContentValues(); 1132 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1133 // Exchange doesn't support TYPE_HOME 1134 mockNameValues.put(Im.TYPE, Im.TYPE_HOME); 1135 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_JABBER); 1136 mockNameValues.put(Im.DATA, "im1"); 1137 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1138 1139 mockNameValues = new ContentValues(); 1140 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1141 // Exchange doesn't support TYPE_WORK 1142 mockNameValues.put(Im.TYPE, Im.TYPE_WORK); 1143 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_YAHOO); 1144 mockNameValues.put(Im.DATA, "im2"); 1145 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1146 1147 mockNameValues = new ContentValues(); 1148 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1149 mockNameValues.put(Im.TYPE, Im.TYPE_OTHER); 1150 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM); 1151 mockNameValues.put(Im.CUSTOM_PROTOCOL, "custom_protocol"); 1152 mockNameValues.put(Im.DATA, "im3"); 1153 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1154 1155 // Exchange can have up to 3 IM entries. This 4th entry should be dropped. 1156 mockNameValues = new ContentValues(); 1157 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1158 mockNameValues.put(Im.TYPE, Im.TYPE_OTHER); 1159 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK); 1160 mockNameValues.put(Im.DATA, "im4"); 1161 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1162 1163 RawContactDelta newState = new RawContactDelta(); 1164 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind); 1165 1166 List<ValuesDelta> list = newState.getMimeEntries(Im.CONTENT_ITEM_TYPE); 1167 assertNotNull(list); 1168 assertEquals(3, list.size()); 1169 1170 assertNotNull(kind.defaultValues.getAsInteger(Im.TYPE)); 1171 1172 int defaultType = kind.defaultValues.getAsInteger(Im.TYPE); 1173 1174 ContentValues outputValues = list.get(0).getAfter(); 1175 // HOME should become default type. 1176 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue()); 1177 assertEquals(Im.PROTOCOL_JABBER, outputValues.getAsInteger(Im.PROTOCOL).intValue()); 1178 assertEquals("im1", outputValues.getAsString(Im.DATA)); 1179 1180 outputValues = list.get(1).getAfter(); 1181 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue()); 1182 assertEquals(Im.PROTOCOL_YAHOO, outputValues.getAsInteger(Im.PROTOCOL).intValue()); 1183 assertEquals("im2", outputValues.getAsString(Im.DATA)); 1184 1185 outputValues = list.get(2).getAfter(); 1186 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue()); 1187 assertEquals(Im.PROTOCOL_CUSTOM, outputValues.getAsInteger(Im.PROTOCOL).intValue()); 1188 assertEquals("custom_protocol", outputValues.getAsString(Im.CUSTOM_PROTOCOL)); 1189 assertEquals("im3", outputValues.getAsString(Im.DATA)); 1190 } 1191 1192 public void testMigratePhoneFromGoogleToExchange() { 1193 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1194 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 1195 DataKind kind = newAccountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 1196 1197 // Create 5 numbers. 1198 // - "1" -- HOME 1199 // - "2" -- WORK 1200 // - "3" -- CUSTOM 1201 // - "4" -- WORK 1202 // - "5" -- WORK_MOBILE 1203 // Then we convert it to Exchange account type. 1204 // - "1" -- HOME 1205 // - "2" -- WORK 1206 // - "3" -- Because CUSTOM is not supported, it'll be changed to the default, MOBILE 1207 // - "4" -- WORK 1208 // - "5" -- WORK_MOBILE not suppoted again, so will be MOBILE. 1209 // But then, Exchange doesn't support multiple MOBILE numbers, so "5" will be removed. 1210 // i.e. the result will be: 1211 // - "1" -- HOME 1212 // - "2" -- WORK 1213 // - "3" -- MOBILE 1214 // - "4" -- WORK 1215 1216 RawContactDelta oldState = new RawContactDelta(); 1217 ContentValues mockNameValues = new ContentValues(); 1218 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1219 mockNameValues.put(Phone.TYPE, Phone.TYPE_HOME); 1220 mockNameValues.put(Phone.NUMBER, "1"); 1221 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1222 mockNameValues = new ContentValues(); 1223 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1224 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK); 1225 mockNameValues.put(Phone.NUMBER, "2"); 1226 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1227 mockNameValues = new ContentValues(); 1228 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1229 // Exchange doesn't support this type. Default to MOBILE 1230 mockNameValues.put(Phone.TYPE, Phone.TYPE_CUSTOM); 1231 mockNameValues.put(Phone.LABEL, "custom_type"); 1232 mockNameValues.put(Phone.NUMBER, "3"); 1233 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1234 mockNameValues = new ContentValues(); 1235 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1236 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK); 1237 mockNameValues.put(Phone.NUMBER, "4"); 1238 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1239 mockNameValues = new ContentValues(); 1240 1241 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1242 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK_MOBILE); 1243 mockNameValues.put(Phone.NUMBER, "5"); 1244 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1245 1246 RawContactDelta newState = new RawContactDelta(); 1247 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind); 1248 1249 List<ValuesDelta> list = newState.getMimeEntries(Phone.CONTENT_ITEM_TYPE); 1250 assertNotNull(list); 1251 assertEquals(4, list.size()); 1252 1253 int defaultType = Phone.TYPE_MOBILE; 1254 1255 ContentValues outputValues = list.get(0).getAfter(); 1256 assertEquals(Phone.TYPE_HOME, outputValues.getAsInteger(Phone.TYPE).intValue()); 1257 assertEquals("1", outputValues.getAsString(Phone.NUMBER)); 1258 outputValues = list.get(1).getAfter(); 1259 assertEquals(Phone.TYPE_WORK, outputValues.getAsInteger(Phone.TYPE).intValue()); 1260 assertEquals("2", outputValues.getAsString(Phone.NUMBER)); 1261 outputValues = list.get(2).getAfter(); 1262 assertEquals(defaultType, outputValues.getAsInteger(Phone.TYPE).intValue()); 1263 assertNull(outputValues.getAsInteger(Phone.LABEL)); 1264 assertEquals("3", outputValues.getAsString(Phone.NUMBER)); 1265 outputValues = list.get(3).getAfter(); 1266 assertEquals(Phone.TYPE_WORK, outputValues.getAsInteger(Phone.TYPE).intValue()); 1267 assertEquals("4", outputValues.getAsString(Phone.NUMBER)); 1268 } 1269 1270 public void testMigrateOrganizationFromGoogleToExchange() { 1271 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1272 AccountType newAccountType = new ExchangeAccountType(getContext(), "", EXCHANGE_ACCT_TYPE); 1273 DataKind kind = newAccountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE); 1274 1275 RawContactDelta oldState = new RawContactDelta(); 1276 ContentValues mockNameValues = new ContentValues(); 1277 mockNameValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); 1278 mockNameValues.put(Organization.COMPANY, "company1"); 1279 mockNameValues.put(Organization.DEPARTMENT, "department1"); 1280 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1281 1282 RawContactDelta newState = new RawContactDelta(); 1283 RawContactModifier.migrateGenericWithoutTypeColumn(oldState, newState, kind); 1284 1285 List<ValuesDelta> list = newState.getMimeEntries(Organization.CONTENT_ITEM_TYPE); 1286 assertNotNull(list); 1287 assertEquals(1, list.size()); 1288 1289 ContentValues outputValues = list.get(0).getAfter(); 1290 assertEquals("company1", outputValues.getAsString(Organization.COMPANY)); 1291 assertEquals("department1", outputValues.getAsString(Organization.DEPARTMENT)); 1292 } 1293} 1294