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