RawContactModifierTests.java revision 851222a96b5d68602fb361ea3527101e893f67e3
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; 18 19import static android.content.ContentProviderOperation.TYPE_DELETE; 20import static android.content.ContentProviderOperation.TYPE_INSERT; 21import static android.content.ContentProviderOperation.TYPE_UPDATE; 22 23import android.content.ContentProviderOperation; 24import android.content.ContentValues; 25import android.net.Uri; 26import android.os.Bundle; 27import android.provider.ContactsContract; 28import android.provider.ContactsContract.CommonDataKinds.Email; 29import android.provider.ContactsContract.CommonDataKinds.Event; 30import android.provider.ContactsContract.CommonDataKinds.Im; 31import android.provider.ContactsContract.CommonDataKinds.Organization; 32import android.provider.ContactsContract.CommonDataKinds.Phone; 33import android.provider.ContactsContract.CommonDataKinds.StructuredName; 34import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 35import android.provider.ContactsContract.Data; 36import android.provider.ContactsContract.Intents.Insert; 37import android.provider.ContactsContract.RawContacts; 38import android.test.AndroidTestCase; 39import android.test.suitebuilder.annotation.LargeTest; 40 41import com.android.contacts.model.AccountTypeManager; 42import com.android.contacts.model.RawContact; 43import com.android.contacts.model.RawContactDelta; 44import com.android.contacts.model.RawContactDelta.ValuesDelta; 45import com.android.contacts.model.RawContactDeltaList; 46import com.android.contacts.model.RawContactModifier; 47import com.android.contacts.model.account.AccountType; 48import com.android.contacts.model.account.AccountType.EditType; 49import com.android.contacts.model.account.ExchangeAccountType; 50import com.android.contacts.model.account.GoogleAccountType; 51import com.android.contacts.model.dataitem.DataKind; 52import com.android.contacts.tests.mocks.ContactsMockContext; 53import com.android.contacts.tests.mocks.MockAccountTypeManager; 54import com.android.contacts.tests.mocks.MockContentProvider; 55import com.google.common.collect.Lists; 56 57import java.util.ArrayList; 58import java.util.List; 59 60/** 61 * Tests for {@link RawContactModifier} to verify that {@link AccountType} 62 * constraints are being enforced correctly. 63 */ 64@LargeTest 65public class RawContactModifierTests extends AndroidTestCase { 66 public static final String TAG = "EntityModifierTests"; 67 68 public static final long VER_FIRST = 100; 69 70 private static final long TEST_ID = 4; 71 private static final String TEST_PHONE = "218-555-1212"; 72 private static final String TEST_NAME = "Adam Young"; 73 private static final String TEST_NAME2 = "Breanne Duren"; 74 private static final String TEST_IM = "example@example.com"; 75 private static final String TEST_POSTAL = "1600 Amphitheatre Parkway"; 76 77 private static final String TEST_ACCOUNT_NAME = "unittest@example.com"; 78 private static final String TEST_ACCOUNT_TYPE = "com.example.unittest"; 79 80 @Override 81 public void setUp() { 82 mContext = getContext(); 83 } 84 85 public static class MockContactsSource extends AccountType { 86 87 MockContactsSource() { 88 try { 89 this.accountType = TEST_ACCOUNT_TYPE; 90 91 final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE, 92 R.string.nameLabelsGroup, -1, true, -1); 93 nameKind.typeOverallMax = 1; 94 addKind(nameKind); 95 96 // Phone allows maximum 2 home, 1 work, and unlimited other, with 97 // constraint of 5 numbers maximum. 98 final DataKind phoneKind = new DataKind( 99 Phone.CONTENT_ITEM_TYPE, -1, 10, true, -1); 100 101 phoneKind.typeOverallMax = 5; 102 phoneKind.typeColumn = Phone.TYPE; 103 phoneKind.typeList = Lists.newArrayList(); 104 phoneKind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2)); 105 phoneKind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1)); 106 phoneKind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true)); 107 phoneKind.typeList.add(new EditType(Phone.TYPE_OTHER, -1)); 108 109 phoneKind.fieldList = Lists.newArrayList(); 110 phoneKind.fieldList.add(new EditField(Phone.NUMBER, -1, -1)); 111 phoneKind.fieldList.add(new EditField(Phone.LABEL, -1, -1)); 112 113 addKind(phoneKind); 114 115 // Email is unlimited 116 final DataKind emailKind = new DataKind( 117 Email.CONTENT_ITEM_TYPE, -1, 10, true, -1); 118 emailKind.typeOverallMax = -1; 119 emailKind.fieldList = Lists.newArrayList(); 120 emailKind.fieldList.add(new EditField(Email.DATA, -1, -1)); 121 addKind(emailKind); 122 123 // IM is only one 124 final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10, 125 true, -1); 126 imKind.typeOverallMax = 1; 127 imKind.fieldList = Lists.newArrayList(); 128 imKind.fieldList.add(new EditField(Im.DATA, -1, -1)); 129 addKind(imKind); 130 131 // Organization is only one 132 final DataKind orgKind = new DataKind( 133 Organization.CONTENT_ITEM_TYPE, -1, 10, true, -1); 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(mContext, 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); 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); 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); 242 assertNotContains(validTypes, typeHome); 243 assertNotContains(validTypes, typeWork); 244 assertNotContains(validTypes, typeOther); 245 } 246 247 /** 248 * Test {@link RawContactModifier#canInsert(RawContactDelta, DataKind)} by 249 * inserting various rows. 250 */ 251 public void testCanInsert() { 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 // Add first home, first work 260 final RawContactDelta state = getRawContact(TEST_ID); 261 RawContactModifier.insertChild(state, kindPhone, typeHome); 262 RawContactModifier.insertChild(state, kindPhone, typeWork); 263 assertTrue("Unable to insert", RawContactModifier.canInsert(state, kindPhone)); 264 265 // Add two other, which puts us just under "5" overall limit 266 RawContactModifier.insertChild(state, kindPhone, typeOther); 267 RawContactModifier.insertChild(state, kindPhone, typeOther); 268 assertTrue("Unable to insert", RawContactModifier.canInsert(state, kindPhone)); 269 270 // Add second home, which should push to snug limit 271 RawContactModifier.insertChild(state, kindPhone, typeHome); 272 assertFalse("Able to insert", RawContactModifier.canInsert(state, kindPhone)); 273 } 274 275 /** 276 * Test 277 * {@link RawContactModifier#getBestValidType(RawContactDelta, DataKind, boolean, int)} 278 * by asserting expected best options in various states. 279 */ 280 public void testBestValidType() { 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 typeFaxWork = RawContactModifier.getType(kindPhone, Phone.TYPE_FAX_WORK); 287 final EditType typeOther = RawContactModifier.getType(kindPhone, Phone.TYPE_OTHER); 288 289 EditType suggested; 290 291 // Default suggestion should be home 292 final RawContactDelta state = getRawContact(TEST_ID); 293 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE); 294 assertEquals("Unexpected suggestion", typeHome, suggested); 295 296 // Add first home, should now suggest work 297 RawContactModifier.insertChild(state, kindPhone, typeHome); 298 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE); 299 assertEquals("Unexpected suggestion", typeWork, suggested); 300 301 // Add work fax, should still suggest work 302 RawContactModifier.insertChild(state, kindPhone, typeFaxWork); 303 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE); 304 assertEquals("Unexpected suggestion", typeWork, suggested); 305 306 // Add other, should still suggest work 307 RawContactModifier.insertChild(state, kindPhone, typeOther); 308 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE); 309 assertEquals("Unexpected suggestion", typeWork, suggested); 310 311 // Add work, now should suggest other 312 RawContactModifier.insertChild(state, kindPhone, typeWork); 313 suggested = RawContactModifier.getBestValidType(state, kindPhone, false, Integer.MIN_VALUE); 314 assertEquals("Unexpected suggestion", typeOther, suggested); 315 } 316 317 public void testIsEmptyEmpty() { 318 final AccountType source = getAccountType(); 319 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 320 321 // Test entirely empty row 322 final ContentValues after = new ContentValues(); 323 final ValuesDelta values = ValuesDelta.fromAfter(after); 324 325 assertTrue("Expected empty", RawContactModifier.isEmpty(values, kindPhone)); 326 } 327 328 public void testIsEmptyDirectFields() { 329 final AccountType source = getAccountType(); 330 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 331 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 332 333 // Test row that has type values, but core fields are empty 334 final RawContactDelta state = getRawContact(TEST_ID); 335 final ValuesDelta values = RawContactModifier.insertChild(state, kindPhone, typeHome); 336 337 assertTrue("Expected empty", RawContactModifier.isEmpty(values, kindPhone)); 338 339 // Insert some data to trigger non-empty state 340 values.put(Phone.NUMBER, TEST_PHONE); 341 342 assertFalse("Expected non-empty", RawContactModifier.isEmpty(values, kindPhone)); 343 } 344 345 public void testTrimEmptySingle() { 346 final AccountType source = getAccountType(); 347 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 348 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 349 350 // Test row that has type values, but core fields are empty 351 final RawContactDelta state = getRawContact(TEST_ID); 352 RawContactModifier.insertChild(state, kindPhone, typeHome); 353 354 // Build diff, expecting insert for data row and update enforcement 355 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 356 state.buildDiff(diff); 357 assertEquals("Unexpected operations", 3, diff.size()); 358 { 359 final ContentProviderOperation oper = diff.get(0); 360 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 361 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 362 } 363 { 364 final ContentProviderOperation oper = diff.get(1); 365 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 366 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 367 } 368 { 369 final ContentProviderOperation oper = diff.get(2); 370 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 371 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 372 } 373 374 // Trim empty rows and try again, expecting delete of overall contact 375 RawContactModifier.trimEmpty(state, source); 376 diff.clear(); 377 state.buildDiff(diff); 378 assertEquals("Unexpected operations", 1, diff.size()); 379 { 380 final ContentProviderOperation oper = diff.get(0); 381 assertEquals("Incorrect type", TYPE_DELETE, oper.getType()); 382 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 383 } 384 } 385 386 public void testTrimEmptySpaces() { 387 final AccountType source = getAccountType(); 388 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 389 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 390 391 // Test row that has type values, but values are spaces 392 final RawContactDelta state = RawContactDeltaListTests.buildBeforeEntity(mContext, TEST_ID, 393 VER_FIRST); 394 final ValuesDelta values = RawContactModifier.insertChild(state, kindPhone, typeHome); 395 values.put(Phone.NUMBER, " "); 396 397 // Build diff, expecting insert for data row and update enforcement 398 RawContactDeltaListTests.assertDiffPattern(state, 399 RawContactDeltaListTests.buildAssertVersion(VER_FIRST), 400 RawContactDeltaListTests.buildUpdateAggregationSuspended(), 401 RawContactDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT, 402 RawContactDeltaListTests.buildDataInsert(values, TEST_ID)), 403 RawContactDeltaListTests.buildUpdateAggregationDefault()); 404 405 // Trim empty rows and try again, expecting delete of overall contact 406 RawContactModifier.trimEmpty(state, source); 407 RawContactDeltaListTests.assertDiffPattern(state, 408 RawContactDeltaListTests.buildAssertVersion(VER_FIRST), 409 RawContactDeltaListTests.buildDelete(RawContacts.CONTENT_URI)); 410 } 411 412 public void testTrimLeaveValid() { 413 final AccountType source = getAccountType(); 414 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 415 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 416 417 // Test row that has type values with valid number 418 final RawContactDelta state = RawContactDeltaListTests.buildBeforeEntity(mContext, TEST_ID, 419 VER_FIRST); 420 final ValuesDelta values = RawContactModifier.insertChild(state, kindPhone, typeHome); 421 values.put(Phone.NUMBER, TEST_PHONE); 422 423 // Build diff, expecting insert for data row and update enforcement 424 RawContactDeltaListTests.assertDiffPattern(state, 425 RawContactDeltaListTests.buildAssertVersion(VER_FIRST), 426 RawContactDeltaListTests.buildUpdateAggregationSuspended(), 427 RawContactDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT, 428 RawContactDeltaListTests.buildDataInsert(values, TEST_ID)), 429 RawContactDeltaListTests.buildUpdateAggregationDefault()); 430 431 // Trim empty rows and try again, expecting no differences 432 RawContactModifier.trimEmpty(state, source); 433 RawContactDeltaListTests.assertDiffPattern(state, 434 RawContactDeltaListTests.buildAssertVersion(VER_FIRST), 435 RawContactDeltaListTests.buildUpdateAggregationSuspended(), 436 RawContactDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT, 437 RawContactDeltaListTests.buildDataInsert(values, TEST_ID)), 438 RawContactDeltaListTests.buildUpdateAggregationDefault()); 439 } 440 441 public void testTrimEmptyUntouched() { 442 final AccountType source = getAccountType(); 443 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 444 RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 445 446 // Build "before" that has empty row 447 final RawContactDelta state = getRawContact(TEST_ID); 448 final ContentValues before = new ContentValues(); 449 before.put(Data._ID, TEST_ID); 450 before.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 451 state.addEntry(ValuesDelta.fromBefore(before)); 452 453 // Build diff, expecting no changes 454 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 455 state.buildDiff(diff); 456 assertEquals("Unexpected operations", 0, diff.size()); 457 458 // Try trimming existing empty, which we shouldn't touch 459 RawContactModifier.trimEmpty(state, source); 460 diff.clear(); 461 state.buildDiff(diff); 462 assertEquals("Unexpected operations", 0, diff.size()); 463 } 464 465 public void testTrimEmptyAfterUpdate() { 466 final AccountType source = getAccountType(); 467 final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 468 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 469 470 // Build "before" that has row with some phone number 471 final ContentValues before = new ContentValues(); 472 before.put(Data._ID, TEST_ID); 473 before.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 474 before.put(kindPhone.typeColumn, typeHome.rawValue); 475 before.put(Phone.NUMBER, TEST_PHONE); 476 final RawContactDelta state = getRawContact(TEST_ID, before); 477 478 // Build diff, expecting no changes 479 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 480 state.buildDiff(diff); 481 assertEquals("Unexpected operations", 0, diff.size()); 482 483 // Now update row by changing number to empty string, expecting single update 484 final ValuesDelta child = state.getEntry(TEST_ID); 485 child.put(Phone.NUMBER, ""); 486 diff.clear(); 487 state.buildDiff(diff); 488 assertEquals("Unexpected operations", 3, diff.size()); 489 { 490 final ContentProviderOperation oper = diff.get(0); 491 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 492 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 493 } 494 { 495 final ContentProviderOperation oper = diff.get(1); 496 assertEquals("Incorrect type", TYPE_UPDATE, oper.getType()); 497 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 498 } 499 { 500 final ContentProviderOperation oper = diff.get(2); 501 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 502 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 503 } 504 505 // Now run trim, which should turn that update into delete 506 RawContactModifier.trimEmpty(state, source); 507 diff.clear(); 508 state.buildDiff(diff); 509 assertEquals("Unexpected operations", 1, diff.size()); 510 { 511 final ContentProviderOperation oper = diff.get(0); 512 assertEquals("Incorrect type", TYPE_DELETE, oper.getType()); 513 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 514 } 515 } 516 517 public void testTrimInsertEmpty() { 518 final AccountType accountType = getAccountType(); 519 final AccountTypeManager accountTypes = getAccountTypes(accountType); 520 final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 521 RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 522 523 // Try creating a contact without any child entries 524 final RawContactDelta state = getRawContact(null); 525 final RawContactDeltaList set = RawContactDeltaList.fromSingle(state); 526 527 // Build diff, expecting single insert 528 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 529 state.buildDiff(diff); 530 assertEquals("Unexpected operations", 2, diff.size()); 531 { 532 final ContentProviderOperation oper = diff.get(0); 533 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 534 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 535 } 536 537 // Trim empty rows and try again, expecting no insert 538 RawContactModifier.trimEmpty(set, accountTypes); 539 diff.clear(); 540 state.buildDiff(diff); 541 assertEquals("Unexpected operations", 0, diff.size()); 542 } 543 544 public void testTrimInsertInsert() { 545 final AccountType accountType = getAccountType(); 546 final AccountTypeManager accountTypes = getAccountTypes(accountType); 547 final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 548 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 549 550 // Try creating a contact with single empty entry 551 final RawContactDelta state = getRawContact(null); 552 RawContactModifier.insertChild(state, kindPhone, typeHome); 553 final RawContactDeltaList set = RawContactDeltaList.fromSingle(state); 554 555 // Build diff, expecting two insert operations 556 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 557 state.buildDiff(diff); 558 assertEquals("Unexpected operations", 3, diff.size()); 559 { 560 final ContentProviderOperation oper = diff.get(0); 561 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 562 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 563 } 564 { 565 final ContentProviderOperation oper = diff.get(1); 566 assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); 567 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 568 } 569 570 // Trim empty rows and try again, expecting silence 571 RawContactModifier.trimEmpty(set, accountTypes); 572 diff.clear(); 573 state.buildDiff(diff); 574 assertEquals("Unexpected operations", 0, diff.size()); 575 } 576 577 public void testTrimUpdateRemain() { 578 final AccountType accountType = getAccountType(); 579 final AccountTypeManager accountTypes = getAccountTypes(accountType); 580 final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 581 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 582 583 // Build "before" with two phone numbers 584 final ContentValues first = new ContentValues(); 585 first.put(Data._ID, TEST_ID); 586 first.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 587 first.put(kindPhone.typeColumn, typeHome.rawValue); 588 first.put(Phone.NUMBER, TEST_PHONE); 589 590 final ContentValues second = new ContentValues(); 591 second.put(Data._ID, TEST_ID); 592 second.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 593 second.put(kindPhone.typeColumn, typeHome.rawValue); 594 second.put(Phone.NUMBER, TEST_PHONE); 595 596 final RawContactDelta state = getRawContact(TEST_ID, first, second); 597 final RawContactDeltaList set = RawContactDeltaList.fromSingle(state); 598 599 // Build diff, expecting no changes 600 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 601 state.buildDiff(diff); 602 assertEquals("Unexpected operations", 0, diff.size()); 603 604 // Now update row by changing number to empty string, expecting single update 605 final ValuesDelta child = state.getEntry(TEST_ID); 606 child.put(Phone.NUMBER, ""); 607 diff.clear(); 608 state.buildDiff(diff); 609 assertEquals("Unexpected operations", 3, diff.size()); 610 { 611 final ContentProviderOperation oper = diff.get(0); 612 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 613 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 614 } 615 { 616 final ContentProviderOperation oper = diff.get(1); 617 assertEquals("Incorrect type", TYPE_UPDATE, oper.getType()); 618 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 619 } 620 { 621 final ContentProviderOperation oper = diff.get(2); 622 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 623 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 624 } 625 626 // Now run trim, which should turn that update into delete 627 RawContactModifier.trimEmpty(set, accountTypes); 628 diff.clear(); 629 state.buildDiff(diff); 630 assertEquals("Unexpected operations", 3, diff.size()); 631 { 632 final ContentProviderOperation oper = diff.get(0); 633 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 634 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 635 } 636 { 637 final ContentProviderOperation oper = diff.get(1); 638 assertEquals("Incorrect type", TYPE_DELETE, oper.getType()); 639 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 640 } 641 { 642 final ContentProviderOperation oper = diff.get(2); 643 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 644 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 645 } 646 } 647 648 public void testTrimUpdateUpdate() { 649 final AccountType accountType = getAccountType(); 650 final AccountTypeManager accountTypes = getAccountTypes(accountType); 651 final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 652 final EditType typeHome = RawContactModifier.getType(kindPhone, Phone.TYPE_HOME); 653 654 // Build "before" with two phone numbers 655 final ContentValues first = new ContentValues(); 656 first.put(Data._ID, TEST_ID); 657 first.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 658 first.put(kindPhone.typeColumn, typeHome.rawValue); 659 first.put(Phone.NUMBER, TEST_PHONE); 660 661 final RawContactDelta state = getRawContact(TEST_ID, first); 662 final RawContactDeltaList set = RawContactDeltaList.fromSingle(state); 663 664 // Build diff, expecting no changes 665 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 666 state.buildDiff(diff); 667 assertEquals("Unexpected operations", 0, diff.size()); 668 669 // Now update row by changing number to empty string, expecting single update 670 final ValuesDelta child = state.getEntry(TEST_ID); 671 child.put(Phone.NUMBER, ""); 672 diff.clear(); 673 state.buildDiff(diff); 674 assertEquals("Unexpected operations", 3, diff.size()); 675 { 676 final ContentProviderOperation oper = diff.get(0); 677 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 678 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 679 } 680 { 681 final ContentProviderOperation oper = diff.get(1); 682 assertEquals("Incorrect type", TYPE_UPDATE, oper.getType()); 683 assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); 684 } 685 { 686 final ContentProviderOperation oper = diff.get(2); 687 assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); 688 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 689 } 690 691 // Now run trim, which should turn into deleting the whole contact 692 RawContactModifier.trimEmpty(set, accountTypes); 693 diff.clear(); 694 state.buildDiff(diff); 695 assertEquals("Unexpected operations", 1, diff.size()); 696 { 697 final ContentProviderOperation oper = diff.get(0); 698 assertEquals("Incorrect type", TYPE_DELETE, oper.getType()); 699 assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); 700 } 701 } 702 703 public void testParseExtrasExistingName() { 704 final AccountType accountType = getAccountType(); 705 706 // Build "before" name 707 final ContentValues first = new ContentValues(); 708 first.put(Data._ID, TEST_ID); 709 first.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 710 first.put(StructuredName.GIVEN_NAME, TEST_NAME); 711 712 // Parse extras, making sure we keep single name 713 final RawContactDelta state = getRawContact(TEST_ID, first); 714 final Bundle extras = new Bundle(); 715 extras.putString(Insert.NAME, TEST_NAME2); 716 RawContactModifier.parseExtras(mContext, accountType, state, extras); 717 718 final int nameCount = state.getMimeEntriesCount(StructuredName.CONTENT_ITEM_TYPE, true); 719 assertEquals("Unexpected names", 1, nameCount); 720 } 721 722 public void testParseExtrasIgnoreLimit() { 723 final AccountType accountType = getAccountType(); 724 725 // Build "before" IM 726 final ContentValues first = new ContentValues(); 727 first.put(Data._ID, TEST_ID); 728 first.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 729 first.put(Im.DATA, TEST_IM); 730 731 final RawContactDelta state = getRawContact(TEST_ID, first); 732 final int beforeCount = state.getMimeEntries(Im.CONTENT_ITEM_TYPE).size(); 733 734 // We should ignore data that doesn't fit account type rules, since account type 735 // only allows single Im 736 final Bundle extras = new Bundle(); 737 extras.putInt(Insert.IM_PROTOCOL, Im.PROTOCOL_GOOGLE_TALK); 738 extras.putString(Insert.IM_HANDLE, TEST_IM); 739 RawContactModifier.parseExtras(mContext, accountType, state, extras); 740 741 final int afterCount = state.getMimeEntries(Im.CONTENT_ITEM_TYPE).size(); 742 assertEquals("Broke account type rules", beforeCount, afterCount); 743 } 744 745 public void testParseExtrasIgnoreUnhandled() { 746 final AccountType accountType = getAccountType(); 747 final RawContactDelta state = getRawContact(TEST_ID); 748 749 // We should silently ignore types unsupported by account type 750 final Bundle extras = new Bundle(); 751 extras.putString(Insert.POSTAL, TEST_POSTAL); 752 RawContactModifier.parseExtras(mContext, accountType, state, extras); 753 754 assertNull("Broke accoun type rules", 755 state.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE)); 756 } 757 758 public void testParseExtrasJobTitle() { 759 final AccountType accountType = getAccountType(); 760 final RawContactDelta state = getRawContact(TEST_ID); 761 762 // Make sure that we create partial Organizations 763 final Bundle extras = new Bundle(); 764 extras.putString(Insert.JOB_TITLE, TEST_NAME); 765 RawContactModifier.parseExtras(mContext, accountType, state, extras); 766 767 final int count = state.getMimeEntries(Organization.CONTENT_ITEM_TYPE).size(); 768 assertEquals("Expected to create organization", 1, count); 769 } 770 771 public void testMigrateWithDisplayNameFromGoogleToExchange1() { 772 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 773 AccountType newAccountType = new ExchangeAccountType(getContext(), ""); 774 DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE); 775 776 ContactsMockContext context = new ContactsMockContext(getContext()); 777 778 RawContactDelta oldState = new RawContactDelta(); 779 ContentValues mockNameValues = new ContentValues(); 780 mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 781 mockNameValues.put(StructuredName.PREFIX, "prefix"); 782 mockNameValues.put(StructuredName.GIVEN_NAME, "given"); 783 mockNameValues.put(StructuredName.MIDDLE_NAME, "middle"); 784 mockNameValues.put(StructuredName.FAMILY_NAME, "family"); 785 mockNameValues.put(StructuredName.SUFFIX, "suffix"); 786 mockNameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "PHONETIC_FAMILY"); 787 mockNameValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "PHONETIC_MIDDLE"); 788 mockNameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "PHONETIC_GIVEN"); 789 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 790 791 RawContactDelta newState = new RawContactDelta(); 792 RawContactModifier.migrateStructuredName(context, oldState, newState, kind); 793 List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE); 794 assertEquals(1, list.size()); 795 796 ContentValues output = list.get(0).getAfter(); 797 assertEquals("prefix", output.getAsString(StructuredName.PREFIX)); 798 assertEquals("given", output.getAsString(StructuredName.GIVEN_NAME)); 799 assertEquals("middle", output.getAsString(StructuredName.MIDDLE_NAME)); 800 assertEquals("family", output.getAsString(StructuredName.FAMILY_NAME)); 801 assertEquals("suffix", output.getAsString(StructuredName.SUFFIX)); 802 // Phonetic middle name isn't supported by Exchange. 803 assertEquals("PHONETIC_FAMILY", output.getAsString(StructuredName.PHONETIC_FAMILY_NAME)); 804 assertEquals("PHONETIC_GIVEN", output.getAsString(StructuredName.PHONETIC_GIVEN_NAME)); 805 } 806 807 public void testMigrateWithDisplayNameFromGoogleToExchange2() { 808 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 809 AccountType newAccountType = new ExchangeAccountType(getContext(), ""); 810 DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE); 811 812 ContactsMockContext context = new ContactsMockContext(getContext()); 813 MockContentProvider provider = context.getContactsProvider(); 814 815 String inputDisplayName = "prefix given middle family suffix"; 816 // The method will ask the provider to split/join StructuredName. 817 Uri uriForBuildDisplayName = 818 ContactsContract.AUTHORITY_URI 819 .buildUpon() 820 .appendPath("complete_name") 821 .appendQueryParameter(StructuredName.DISPLAY_NAME, inputDisplayName) 822 .build(); 823 provider.expectQuery(uriForBuildDisplayName) 824 .returnRow("prefix", "given", "middle", "family", "suffix") 825 .withProjection(StructuredName.PREFIX, StructuredName.GIVEN_NAME, 826 StructuredName.MIDDLE_NAME, StructuredName.FAMILY_NAME, 827 StructuredName.SUFFIX); 828 829 RawContactDelta oldState = new RawContactDelta(); 830 ContentValues mockNameValues = new ContentValues(); 831 mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 832 mockNameValues.put(StructuredName.DISPLAY_NAME, inputDisplayName); 833 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 834 835 RawContactDelta newState = new RawContactDelta(); 836 RawContactModifier.migrateStructuredName(context, oldState, newState, kind); 837 List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE); 838 assertEquals(1, list.size()); 839 840 ContentValues outputValues = list.get(0).getAfter(); 841 assertEquals("prefix", outputValues.getAsString(StructuredName.PREFIX)); 842 assertEquals("given", outputValues.getAsString(StructuredName.GIVEN_NAME)); 843 assertEquals("middle", outputValues.getAsString(StructuredName.MIDDLE_NAME)); 844 assertEquals("family", outputValues.getAsString(StructuredName.FAMILY_NAME)); 845 assertEquals("suffix", outputValues.getAsString(StructuredName.SUFFIX)); 846 } 847 848 public void testMigrateWithStructuredNameFromExchangeToGoogle() { 849 AccountType oldAccountType = new ExchangeAccountType(getContext(), ""); 850 AccountType newAccountType = new GoogleAccountType(getContext(), ""); 851 DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE); 852 853 ContactsMockContext context = new ContactsMockContext(getContext()); 854 MockContentProvider provider = context.getContactsProvider(); 855 856 // The method will ask the provider to split/join StructuredName. 857 Uri uriForBuildDisplayName = 858 ContactsContract.AUTHORITY_URI 859 .buildUpon() 860 .appendPath("complete_name") 861 .appendQueryParameter(StructuredName.PREFIX, "prefix") 862 .appendQueryParameter(StructuredName.GIVEN_NAME, "given") 863 .appendQueryParameter(StructuredName.MIDDLE_NAME, "middle") 864 .appendQueryParameter(StructuredName.FAMILY_NAME, "family") 865 .appendQueryParameter(StructuredName.SUFFIX, "suffix") 866 .build(); 867 provider.expectQuery(uriForBuildDisplayName) 868 .returnRow("prefix given middle family suffix") 869 .withProjection(StructuredName.DISPLAY_NAME); 870 871 RawContactDelta oldState = new RawContactDelta(); 872 ContentValues mockNameValues = new ContentValues(); 873 mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 874 mockNameValues.put(StructuredName.PREFIX, "prefix"); 875 mockNameValues.put(StructuredName.GIVEN_NAME, "given"); 876 mockNameValues.put(StructuredName.MIDDLE_NAME, "middle"); 877 mockNameValues.put(StructuredName.FAMILY_NAME, "family"); 878 mockNameValues.put(StructuredName.SUFFIX, "suffix"); 879 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 880 881 RawContactDelta newState = new RawContactDelta(); 882 RawContactModifier.migrateStructuredName(context, oldState, newState, kind); 883 884 List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE); 885 assertNotNull(list); 886 assertEquals(1, list.size()); 887 ContentValues outputValues = list.get(0).getAfter(); 888 assertEquals("prefix given middle family suffix", 889 outputValues.getAsString(StructuredName.DISPLAY_NAME)); 890 } 891 892 public void testMigratePostalFromGoogleToExchange() { 893 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 894 AccountType newAccountType = new ExchangeAccountType(getContext(), ""); 895 DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE); 896 897 RawContactDelta oldState = new RawContactDelta(); 898 ContentValues mockNameValues = new ContentValues(); 899 mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 900 mockNameValues.put(StructuredPostal.FORMATTED_ADDRESS, "formatted_address"); 901 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 902 903 RawContactDelta newState = new RawContactDelta(); 904 RawContactModifier.migratePostal(oldState, newState, kind); 905 906 List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE); 907 assertNotNull(list); 908 assertEquals(1, list.size()); 909 ContentValues outputValues = list.get(0).getAfter(); 910 // FORMATTED_ADDRESS isn't supported by Exchange. 911 assertNull(outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS)); 912 assertEquals("formatted_address", outputValues.getAsString(StructuredPostal.STREET)); 913 } 914 915 public void testMigratePostalFromExchangeToGoogle() { 916 AccountType oldAccountType = new ExchangeAccountType(getContext(), ""); 917 AccountType newAccountType = new GoogleAccountType(getContext(), ""); 918 DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE); 919 920 RawContactDelta oldState = new RawContactDelta(); 921 ContentValues mockNameValues = new ContentValues(); 922 mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 923 mockNameValues.put(StructuredPostal.COUNTRY, "country"); 924 mockNameValues.put(StructuredPostal.POSTCODE, "postcode"); 925 mockNameValues.put(StructuredPostal.REGION, "region"); 926 mockNameValues.put(StructuredPostal.CITY, "city"); 927 mockNameValues.put(StructuredPostal.STREET, "street"); 928 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 929 930 RawContactDelta newState = new RawContactDelta(); 931 RawContactModifier.migratePostal(oldState, newState, kind); 932 933 List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE); 934 assertNotNull(list); 935 assertEquals(1, list.size()); 936 ContentValues outputValues = list.get(0).getAfter(); 937 938 // Check FORMATTED_ADDRESS contains all info. 939 String formattedAddress = outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS); 940 assertNotNull(formattedAddress); 941 assertTrue(formattedAddress.contains("country")); 942 assertTrue(formattedAddress.contains("postcode")); 943 assertTrue(formattedAddress.contains("region")); 944 assertTrue(formattedAddress.contains("postcode")); 945 assertTrue(formattedAddress.contains("city")); 946 assertTrue(formattedAddress.contains("street")); 947 } 948 949 public void testMigrateEventFromGoogleToExchange1() { 950 testMigrateEventCommon(new GoogleAccountType(getContext(), ""), 951 new ExchangeAccountType(getContext(), "")); 952 } 953 954 public void testMigrateEventFromExchangeToGoogle() { 955 testMigrateEventCommon(new ExchangeAccountType(getContext(), ""), 956 new GoogleAccountType(getContext(), "")); 957 } 958 959 private void testMigrateEventCommon(AccountType oldAccountType, AccountType newAccountType) { 960 DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE); 961 962 RawContactDelta oldState = new RawContactDelta(); 963 ContentValues mockNameValues = new ContentValues(); 964 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); 965 mockNameValues.put(Event.START_DATE, "1972-02-08"); 966 mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY); 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 assertEquals("1972-02-08", outputValues.getAsString(Event.START_DATE)); 978 assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue()); 979 } 980 981 public void testMigrateEventFromGoogleToExchange2() { 982 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 983 AccountType newAccountType = new ExchangeAccountType(getContext(), ""); 984 DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE); 985 986 RawContactDelta oldState = new RawContactDelta(); 987 ContentValues mockNameValues = new ContentValues(); 988 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); 989 // No year format is not supported by Exchange. 990 mockNameValues.put(Event.START_DATE, "--06-01"); 991 mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY); 992 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 993 mockNameValues = new ContentValues(); 994 mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); 995 mockNameValues.put(Event.START_DATE, "1980-08-02"); 996 // Anniversary is not supported by Exchange 997 mockNameValues.put(Event.TYPE, Event.TYPE_ANNIVERSARY); 998 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 999 1000 RawContactDelta newState = new RawContactDelta(); 1001 RawContactModifier.migrateEvent(oldState, newState, kind, 1990); 1002 1003 List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE); 1004 assertNotNull(list); 1005 assertEquals(1, list.size()); // Anniversary should be dropped. 1006 ContentValues outputValues = list.get(0).getAfter(); 1007 1008 // Default year should be used. 1009 assertEquals("1990-06-01", outputValues.getAsString(Event.START_DATE)); 1010 assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue()); 1011 } 1012 1013 public void testMigrateEmailFromGoogleToExchange() { 1014 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1015 AccountType newAccountType = new ExchangeAccountType(getContext(), ""); 1016 DataKind kind = newAccountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE); 1017 1018 RawContactDelta oldState = new RawContactDelta(); 1019 ContentValues mockNameValues = new ContentValues(); 1020 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1021 mockNameValues.put(Email.TYPE, Email.TYPE_CUSTOM); 1022 mockNameValues.put(Email.LABEL, "custom_type"); 1023 mockNameValues.put(Email.ADDRESS, "address1"); 1024 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1025 mockNameValues = new ContentValues(); 1026 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1027 mockNameValues.put(Email.TYPE, Email.TYPE_HOME); 1028 mockNameValues.put(Email.ADDRESS, "address2"); 1029 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1030 mockNameValues = new ContentValues(); 1031 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1032 mockNameValues.put(Email.TYPE, Email.TYPE_WORK); 1033 mockNameValues.put(Email.ADDRESS, "address3"); 1034 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1035 // Exchange can have up to 3 email entries. This 4th entry should be dropped. 1036 mockNameValues = new ContentValues(); 1037 mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1038 mockNameValues.put(Email.TYPE, Email.TYPE_OTHER); 1039 mockNameValues.put(Email.ADDRESS, "address4"); 1040 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1041 1042 RawContactDelta newState = new RawContactDelta(); 1043 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind); 1044 1045 List<ValuesDelta> list = newState.getMimeEntries(Email.CONTENT_ITEM_TYPE); 1046 assertNotNull(list); 1047 assertEquals(3, list.size()); 1048 1049 ContentValues outputValues = list.get(0).getAfter(); 1050 assertEquals(Email.TYPE_CUSTOM, outputValues.getAsInteger(Email.TYPE).intValue()); 1051 assertEquals("custom_type", outputValues.getAsString(Email.LABEL)); 1052 assertEquals("address1", outputValues.getAsString(Email.ADDRESS)); 1053 1054 outputValues = list.get(1).getAfter(); 1055 assertEquals(Email.TYPE_HOME, outputValues.getAsInteger(Email.TYPE).intValue()); 1056 assertEquals("address2", outputValues.getAsString(Email.ADDRESS)); 1057 1058 outputValues = list.get(2).getAfter(); 1059 assertEquals(Email.TYPE_WORK, outputValues.getAsInteger(Email.TYPE).intValue()); 1060 assertEquals("address3", outputValues.getAsString(Email.ADDRESS)); 1061 } 1062 1063 public void testMigrateImFromGoogleToExchange() { 1064 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1065 AccountType newAccountType = new ExchangeAccountType(getContext(), ""); 1066 DataKind kind = newAccountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE); 1067 1068 RawContactDelta oldState = new RawContactDelta(); 1069 ContentValues mockNameValues = new ContentValues(); 1070 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1071 // Exchange doesn't support TYPE_HOME 1072 mockNameValues.put(Im.TYPE, Im.TYPE_HOME); 1073 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_JABBER); 1074 mockNameValues.put(Im.DATA, "im1"); 1075 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1076 1077 mockNameValues = new ContentValues(); 1078 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1079 // Exchange doesn't support TYPE_WORK 1080 mockNameValues.put(Im.TYPE, Im.TYPE_WORK); 1081 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_YAHOO); 1082 mockNameValues.put(Im.DATA, "im2"); 1083 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1084 1085 mockNameValues = new ContentValues(); 1086 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1087 mockNameValues.put(Im.TYPE, Im.TYPE_OTHER); 1088 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM); 1089 mockNameValues.put(Im.CUSTOM_PROTOCOL, "custom_protocol"); 1090 mockNameValues.put(Im.DATA, "im3"); 1091 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1092 1093 // Exchange can have up to 3 IM entries. This 4th entry should be dropped. 1094 mockNameValues = new ContentValues(); 1095 mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1096 mockNameValues.put(Im.TYPE, Im.TYPE_OTHER); 1097 mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK); 1098 mockNameValues.put(Im.DATA, "im4"); 1099 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1100 1101 RawContactDelta newState = new RawContactDelta(); 1102 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind); 1103 1104 List<ValuesDelta> list = newState.getMimeEntries(Im.CONTENT_ITEM_TYPE); 1105 assertNotNull(list); 1106 assertEquals(3, list.size()); 1107 1108 assertNotNull(kind.defaultValues.getAsInteger(Im.TYPE)); 1109 1110 int defaultType = kind.defaultValues.getAsInteger(Im.TYPE); 1111 1112 ContentValues outputValues = list.get(0).getAfter(); 1113 // HOME should become default type. 1114 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue()); 1115 assertEquals(Im.PROTOCOL_JABBER, outputValues.getAsInteger(Im.PROTOCOL).intValue()); 1116 assertEquals("im1", outputValues.getAsString(Im.DATA)); 1117 1118 outputValues = list.get(1).getAfter(); 1119 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue()); 1120 assertEquals(Im.PROTOCOL_YAHOO, outputValues.getAsInteger(Im.PROTOCOL).intValue()); 1121 assertEquals("im2", outputValues.getAsString(Im.DATA)); 1122 1123 outputValues = list.get(2).getAfter(); 1124 assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue()); 1125 assertEquals(Im.PROTOCOL_CUSTOM, outputValues.getAsInteger(Im.PROTOCOL).intValue()); 1126 assertEquals("custom_protocol", outputValues.getAsString(Im.CUSTOM_PROTOCOL)); 1127 assertEquals("im3", outputValues.getAsString(Im.DATA)); 1128 } 1129 1130 public void testMigratePhoneFromGoogleToExchange() { 1131 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1132 AccountType newAccountType = new ExchangeAccountType(getContext(), ""); 1133 DataKind kind = newAccountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 1134 1135 // Create 5 numbers. 1136 // - "1" -- HOME 1137 // - "2" -- WORK 1138 // - "3" -- CUSTOM 1139 // - "4" -- WORK 1140 // - "5" -- WORK_MOBILE 1141 // Then we convert it to Exchange account type. 1142 // - "1" -- HOME 1143 // - "2" -- WORK 1144 // - "3" -- Because CUSTOM is not supported, it'll be changed to the default, MOBILE 1145 // - "4" -- WORK 1146 // - "5" -- WORK_MOBILE not suppoted again, so will be MOBILE. 1147 // But then, Exchange doesn't support multiple MOBILE numbers, so "5" will be removed. 1148 // i.e. the result will be: 1149 // - "1" -- HOME 1150 // - "2" -- WORK 1151 // - "3" -- MOBILE 1152 // - "4" -- WORK 1153 1154 RawContactDelta oldState = new RawContactDelta(); 1155 ContentValues mockNameValues = new ContentValues(); 1156 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1157 mockNameValues.put(Phone.TYPE, Phone.TYPE_HOME); 1158 mockNameValues.put(Phone.NUMBER, "1"); 1159 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1160 mockNameValues = new ContentValues(); 1161 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1162 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK); 1163 mockNameValues.put(Phone.NUMBER, "2"); 1164 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1165 mockNameValues = new ContentValues(); 1166 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1167 // Exchange doesn't support this type. Default to MOBILE 1168 mockNameValues.put(Phone.TYPE, Phone.TYPE_CUSTOM); 1169 mockNameValues.put(Phone.LABEL, "custom_type"); 1170 mockNameValues.put(Phone.NUMBER, "3"); 1171 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1172 mockNameValues = new ContentValues(); 1173 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1174 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK); 1175 mockNameValues.put(Phone.NUMBER, "4"); 1176 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1177 mockNameValues = new ContentValues(); 1178 1179 mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1180 mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK_MOBILE); 1181 mockNameValues.put(Phone.NUMBER, "5"); 1182 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1183 1184 RawContactDelta newState = new RawContactDelta(); 1185 RawContactModifier.migrateGenericWithTypeColumn(oldState, newState, kind); 1186 1187 List<ValuesDelta> list = newState.getMimeEntries(Phone.CONTENT_ITEM_TYPE); 1188 assertNotNull(list); 1189 assertEquals(4, list.size()); 1190 1191 int defaultType = Phone.TYPE_MOBILE; 1192 1193 ContentValues outputValues = list.get(0).getAfter(); 1194 assertEquals(Phone.TYPE_HOME, outputValues.getAsInteger(Phone.TYPE).intValue()); 1195 assertEquals("1", outputValues.getAsString(Phone.NUMBER)); 1196 outputValues = list.get(1).getAfter(); 1197 assertEquals(Phone.TYPE_WORK, outputValues.getAsInteger(Phone.TYPE).intValue()); 1198 assertEquals("2", outputValues.getAsString(Phone.NUMBER)); 1199 outputValues = list.get(2).getAfter(); 1200 assertEquals(defaultType, outputValues.getAsInteger(Phone.TYPE).intValue()); 1201 assertNull(outputValues.getAsInteger(Phone.LABEL)); 1202 assertEquals("3", outputValues.getAsString(Phone.NUMBER)); 1203 outputValues = list.get(3).getAfter(); 1204 assertEquals(Phone.TYPE_WORK, outputValues.getAsInteger(Phone.TYPE).intValue()); 1205 assertEquals("4", outputValues.getAsString(Phone.NUMBER)); 1206 } 1207 1208 public void testMigrateOrganizationFromGoogleToExchange() { 1209 AccountType oldAccountType = new GoogleAccountType(getContext(), ""); 1210 AccountType newAccountType = new ExchangeAccountType(getContext(), ""); 1211 DataKind kind = newAccountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE); 1212 1213 RawContactDelta oldState = new RawContactDelta(); 1214 ContentValues mockNameValues = new ContentValues(); 1215 mockNameValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); 1216 mockNameValues.put(Organization.COMPANY, "company1"); 1217 mockNameValues.put(Organization.DEPARTMENT, "department1"); 1218 oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); 1219 1220 RawContactDelta newState = new RawContactDelta(); 1221 RawContactModifier.migrateGenericWithoutTypeColumn(oldState, newState, kind); 1222 1223 List<ValuesDelta> list = newState.getMimeEntries(Organization.CONTENT_ITEM_TYPE); 1224 assertNotNull(list); 1225 assertEquals(1, list.size()); 1226 1227 ContentValues outputValues = list.get(0).getAfter(); 1228 assertEquals("company1", outputValues.getAsString(Organization.COMPANY)); 1229 assertEquals("department1", outputValues.getAsString(Organization.DEPARTMENT)); 1230 } 1231} 1232