Home | History | Annotate | Download | only in common
      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.common;
     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.common.model.RawContact;
     43 import com.android.contacts.common.model.RawContactDelta;
     44 import com.android.contacts.common.model.ValuesDelta;
     45 import com.android.contacts.common.model.RawContactDeltaList;
     46 import com.android.contacts.common.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.common.test.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