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