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