Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2010 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.model;
     18 
     19 import android.content.ContentUris;
     20 import android.net.Uri;
     21 import android.provider.ContactsContract;
     22 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
     23 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     24 import android.provider.ContactsContract.Contacts;
     25 import android.provider.ContactsContract.Data;
     26 import android.provider.ContactsContract.DisplayNameSources;
     27 import android.provider.ContactsContract.RawContacts;
     28 import android.provider.ContactsContract.StatusUpdates;
     29 import android.test.LoaderTestCase;
     30 import android.test.suitebuilder.annotation.LargeTest;
     31 
     32 import com.android.contacts.common.model.AccountTypeManager;
     33 import com.android.contacts.common.test.mocks.ContactsMockContext;
     34 import com.android.contacts.common.test.mocks.MockContentProvider;
     35 import com.android.contacts.common.model.account.AccountType;
     36 import com.android.contacts.common.model.account.AccountWithDataSet;
     37 import com.android.contacts.common.model.account.BaseAccountType;
     38 import com.android.contacts.common.testing.InjectedServices;
     39 import com.android.contacts.common.test.mocks.MockAccountTypeManager;
     40 import com.android.contacts.common.util.Constants;
     41 
     42 import org.json.JSONException;
     43 import org.json.JSONObject;
     44 
     45 /**
     46  * Runs ContactLoader tests for the the contact-detail and editor view.
     47  */
     48 @LargeTest
     49 public class ContactLoaderTest extends LoaderTestCase {
     50     private static final long CONTACT_ID = 1;
     51     private static final long RAW_CONTACT_ID = 11;
     52     private static final long DATA_ID = 21;
     53     private static final String LOOKUP_KEY = "aa%12%@!";
     54 
     55     private ContactsMockContext mMockContext;
     56     private MockContentProvider mContactsProvider;
     57 
     58     @Override
     59     protected void setUp() throws Exception {
     60         super.setUp();
     61         mMockContext = new ContactsMockContext(getContext());
     62         mContactsProvider = mMockContext.getContactsProvider();
     63 
     64         InjectedServices services = new InjectedServices();
     65         AccountType accountType = new BaseAccountType() {
     66             @Override
     67             public boolean areContactsWritable() {
     68                 return false;
     69             }
     70         };
     71         accountType.accountType = "mockAccountType";
     72 
     73         AccountWithDataSet account =
     74                 new AccountWithDataSet("mockAccountName", "mockAccountType", null);
     75 
     76         AccountTypeManager.setInstanceForTest(
     77                 new MockAccountTypeManager(
     78                         new AccountType[]{accountType}, new AccountWithDataSet[]{account}));
     79     }
     80 
     81     @Override
     82     protected void tearDown() throws Exception {
     83         mMockContext = null;
     84         mContactsProvider = null;
     85         super.tearDown();
     86     }
     87 
     88     private Contact assertLoadContact(Uri uri) {
     89         final ContactLoader loader = new ContactLoader(mMockContext, uri, true);
     90         return getLoaderResultSynchronously(loader);
     91     }
     92 
     93     public void testNullUri() {
     94         Contact result = assertLoadContact(null);
     95         assertTrue(result.isError());
     96     }
     97 
     98     public void testEmptyUri() {
     99         Contact result = assertLoadContact(Uri.EMPTY);
    100         assertTrue(result.isError());
    101     }
    102 
    103     public void testInvalidUri() {
    104         Contact result = assertLoadContact(Uri.parse("content://wtf"));
    105         assertTrue(result.isError());
    106     }
    107 
    108     public void testLoadContactWithContactIdUri() {
    109         // Use content Uris that only contain the ID
    110         final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
    111         final Uri entityUri = Uri.withAppendedPath(baseUri, Contacts.Entity.CONTENT_DIRECTORY);
    112         final Uri lookupUri = ContentUris.withAppendedId(
    113                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
    114                 CONTACT_ID);
    115 
    116         ContactQueries queries = new ContactQueries();
    117         mContactsProvider.expectTypeQuery(baseUri, Contacts.CONTENT_ITEM_TYPE);
    118         queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
    119 
    120         Contact contact = assertLoadContact(baseUri);
    121 
    122         assertEquals(CONTACT_ID, contact.getId());
    123         assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
    124         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
    125         assertEquals(LOOKUP_KEY, contact.getLookupKey());
    126         assertEquals(lookupUri, contact.getLookupUri());
    127         assertEquals(1, contact.getRawContacts().size());
    128         assertEquals(1, contact.getStatuses().size());
    129         mContactsProvider.verify();
    130     }
    131 
    132     public void testLoadContactWithOldStyleUri() {
    133         // Use content Uris that only contain the ID but use the format used in Donut
    134         final Uri legacyUri = ContentUris.withAppendedId(
    135                 Uri.parse("content://contacts"), RAW_CONTACT_ID);
    136         final Uri rawContactUri = ContentUris.withAppendedId(
    137                 RawContacts.CONTENT_URI, RAW_CONTACT_ID);
    138         final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
    139         final Uri lookupUri = ContentUris.withAppendedId(
    140                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
    141                 CONTACT_ID);
    142         final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    143 
    144         ContactQueries queries = new ContactQueries();
    145         queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, CONTACT_ID, LOOKUP_KEY);
    146         queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
    147 
    148         Contact contact = assertLoadContact(legacyUri);
    149 
    150         assertEquals(CONTACT_ID, contact.getId());
    151         assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
    152         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
    153         assertEquals(LOOKUP_KEY, contact.getLookupKey());
    154         assertEquals(lookupUri, contact.getLookupUri());
    155         assertEquals(1, contact.getRawContacts().size());
    156         assertEquals(1, contact.getStatuses().size());
    157         mContactsProvider.verify();
    158     }
    159 
    160     public void testLoadContactWithRawContactIdUri() {
    161         // Use content Uris that only contain the ID but use the format used in Donut
    162         final Uri rawContactUri = ContentUris.withAppendedId(
    163                 RawContacts.CONTENT_URI, RAW_CONTACT_ID);
    164         final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
    165         final Uri lookupUri = ContentUris.withAppendedId(
    166                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
    167                 CONTACT_ID);
    168         final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    169 
    170         ContactQueries queries = new ContactQueries();
    171         mContactsProvider.expectTypeQuery(rawContactUri, RawContacts.CONTENT_ITEM_TYPE);
    172         queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, CONTACT_ID, LOOKUP_KEY);
    173         queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
    174 
    175         Contact contact = assertLoadContact(rawContactUri);
    176 
    177         assertEquals(CONTACT_ID, contact.getId());
    178         assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
    179         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
    180         assertEquals(LOOKUP_KEY, contact.getLookupKey());
    181         assertEquals(lookupUri, contact.getLookupUri());
    182         assertEquals(1, contact.getRawContacts().size());
    183         assertEquals(1, contact.getStatuses().size());
    184         mContactsProvider.verify();
    185     }
    186 
    187     public void testLoadContactWithContactLookupUri() {
    188         // Use lookup-style Uris that do not contain the Contact-ID
    189         final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
    190         final Uri lookupNoIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY);
    191         final Uri lookupUri = ContentUris.withAppendedId(lookupNoIdUri, CONTACT_ID);
    192         final Uri entityUri = Uri.withAppendedPath(
    193                 lookupNoIdUri, Contacts.Entity.CONTENT_DIRECTORY);
    194 
    195         ContactQueries queries = new ContactQueries();
    196         mContactsProvider.expectTypeQuery(lookupNoIdUri, Contacts.CONTENT_ITEM_TYPE);
    197         queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
    198 
    199         Contact contact = assertLoadContact(lookupNoIdUri);
    200 
    201         assertEquals(CONTACT_ID, contact.getId());
    202         assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
    203         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
    204         assertEquals(LOOKUP_KEY, contact.getLookupKey());
    205         assertEquals(lookupUri, contact.getLookupUri());
    206         assertEquals(1, contact.getRawContacts().size());
    207         assertEquals(1, contact.getStatuses().size());
    208         mContactsProvider.verify();
    209     }
    210 
    211     public void testLoadContactWithContactLookupAndIdUri() {
    212         // Use lookup-style Uris that also contain the Contact-ID
    213         final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
    214         final Uri lookupUri = ContentUris.withAppendedId(
    215                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
    216                 CONTACT_ID);
    217         final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    218 
    219         ContactQueries queries = new ContactQueries();
    220         mContactsProvider.expectTypeQuery(lookupUri, Contacts.CONTENT_ITEM_TYPE);
    221         queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
    222 
    223         Contact contact = assertLoadContact(lookupUri);
    224 
    225         assertEquals(CONTACT_ID, contact.getId());
    226         assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
    227         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
    228         assertEquals(LOOKUP_KEY, contact.getLookupKey());
    229         assertEquals(lookupUri, contact.getLookupUri());
    230         assertEquals(1, contact.getRawContacts().size());
    231         assertEquals(1, contact.getStatuses().size());
    232         mContactsProvider.verify();
    233     }
    234 
    235     public void testLoadContactWithContactLookupWithIncorrectIdUri() {
    236         // Use lookup-style Uris that contain incorrect Contact-ID
    237         // (we want to ensure that still the correct contact is chosen)
    238         final long wrongContactId = 2;
    239         final long wrongRawContactId = 12;
    240 
    241         final String wrongLookupKey = "ab%12%@!";
    242         final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
    243         final Uri wrongBaseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, wrongContactId);
    244         final Uri lookupUri = ContentUris.withAppendedId(
    245                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
    246                 CONTACT_ID);
    247         final Uri lookupWithWrongIdUri = ContentUris.withAppendedId(
    248                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
    249                 wrongContactId);
    250         final Uri entityUri = Uri.withAppendedPath(lookupWithWrongIdUri,
    251                 Contacts.Entity.CONTENT_DIRECTORY);
    252 
    253         ContactQueries queries = new ContactQueries();
    254         mContactsProvider.expectTypeQuery(lookupWithWrongIdUri, Contacts.CONTENT_ITEM_TYPE);
    255         queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
    256 
    257         Contact contact = assertLoadContact(lookupWithWrongIdUri);
    258 
    259         assertEquals(CONTACT_ID, contact.getId());
    260         assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
    261         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
    262         assertEquals(LOOKUP_KEY, contact.getLookupKey());
    263         assertEquals(lookupUri, contact.getLookupUri());
    264         assertEquals(1, contact.getRawContacts().size());
    265         assertEquals(1, contact.getStatuses().size());
    266 
    267         mContactsProvider.verify();
    268     }
    269 
    270     public void testLoadContactReturnDirectoryContactWithoutDisplayName() throws JSONException {
    271         // Use lookup-style Uri that contains encoded json object which encapsulates the
    272         // directory contact. The test json object is:
    273         // {
    274         //   display_name_source": 40,
    275         //   "vnd.android.cursor.item\/contact":{"email":{"data1":"test (at) google.com" }}
    276         // }
    277         JSONObject itemJson = new JSONObject();
    278         itemJson.put("email", new JSONObject().put("data1", "test (at) google.com"));
    279         JSONObject json = new JSONObject();
    280         json.put(Contacts.NAME_RAW_CONTACT_ID, CONTACT_ID);
    281         json.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
    282         json.put(Contacts.CONTENT_ITEM_TYPE, itemJson);
    283 
    284         final Uri lookupUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
    285                 .encodedFragment(json.toString())
    286                 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, "1")
    287                 .appendPath(Constants.LOOKUP_URI_ENCODED).build();
    288 
    289         mContactsProvider.expectTypeQuery(lookupUri, Contacts.CONTENT_ITEM_TYPE);
    290         Contact contact = assertLoadContact(lookupUri);
    291 
    292         assertEquals(-1, contact.getId());
    293         assertEquals(-1, contact.getNameRawContactId());
    294         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
    295         assertEquals("", contact.getDisplayName());
    296         assertEquals(lookupUri, contact.getLookupUri());
    297         assertEquals(1, contact.getRawContacts().size());
    298         mContactsProvider.verify();
    299     }
    300 
    301     class ContactQueries {
    302         public void fetchAllData(
    303                 Uri baseUri, long contactId, long rawContactId, long dataId, String encodedLookup) {
    304             mContactsProvider.expectQuery(baseUri)
    305                     .withProjection(new String[] {
    306                         Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE,
    307                         Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME,
    308                         Contacts.DISPLAY_NAME_ALTERNATIVE, Contacts.PHONETIC_NAME,
    309                         Contacts.PHOTO_ID, Contacts.STARRED, Contacts.CONTACT_PRESENCE,
    310                         Contacts.CONTACT_STATUS, Contacts.CONTACT_STATUS_TIMESTAMP,
    311                         Contacts.CONTACT_STATUS_RES_PACKAGE, Contacts.CONTACT_STATUS_LABEL,
    312 
    313                         Contacts.Entity.CONTACT_ID,
    314                         Contacts.Entity.RAW_CONTACT_ID,
    315 
    316                         RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE,
    317                         RawContacts.DATA_SET,
    318                         RawContacts.DIRTY, RawContacts.VERSION, RawContacts.SOURCE_ID,
    319                         RawContacts.SYNC1, RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4,
    320                         RawContacts.DELETED,
    321 
    322                         Contacts.Entity.DATA_ID,
    323 
    324                         Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
    325                         Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10,
    326                         Data.DATA11, Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
    327                         Data.SYNC1, Data.SYNC2, Data.SYNC3, Data.SYNC4,
    328                         Data.DATA_VERSION, Data.IS_PRIMARY,
    329                         Data.IS_SUPER_PRIMARY, Data.MIMETYPE,
    330 
    331                         GroupMembership.GROUP_SOURCE_ID,
    332 
    333                         Data.PRESENCE, Data.CHAT_CAPABILITY,
    334                         Data.STATUS, Data.STATUS_RES_PACKAGE, Data.STATUS_ICON,
    335                         Data.STATUS_LABEL, Data.STATUS_TIMESTAMP,
    336 
    337                         Contacts.PHOTO_URI,
    338 
    339                         Contacts.SEND_TO_VOICEMAIL,
    340                         Contacts.CUSTOM_RINGTONE,
    341                         Contacts.IS_USER_PROFILE,
    342 
    343                         Data.TIMES_USED,
    344                         Data.LAST_TIME_USED,
    345                     })
    346                     .withSortOrder(Contacts.Entity.RAW_CONTACT_ID)
    347                     .returnRow(
    348                         rawContactId, 40,
    349                         "aa%12%@!", "John Doe", "Doe, John", "jdo",
    350                         0, 0, StatusUpdates.AVAILABLE,
    351                         "Having lunch", 0,
    352                         "mockPkg1", 10,
    353 
    354                         contactId,
    355                         rawContactId,
    356 
    357                         "mockAccountName", "mockAccountType", null,
    358                         0, 1, 0,
    359                         "sync1", "sync2", "sync3", "sync4",
    360                         0,
    361 
    362                         dataId,
    363 
    364                         "dat1", "dat2", "dat3", "dat4", "dat5",
    365                         "dat6", "dat7", "dat8", "dat9", "dat10",
    366                         "dat11", "dat12", "dat13", "dat14", "dat15",
    367                         "syn1", "syn2", "syn3", "syn4",
    368 
    369                         0, 0,
    370                         0, StructuredName.CONTENT_ITEM_TYPE,
    371 
    372                         "groupId",
    373 
    374                         StatusUpdates.INVISIBLE, null,
    375                         "Having dinner", "mockPkg3", 0,
    376                         20, 0,
    377 
    378                         "content:some.photo.uri",
    379 
    380                         0,
    381                         null,
    382                         0,
    383 
    384                         0,
    385                         0
    386                     );
    387         }
    388 
    389         void fetchLookupAndId(final Uri sourceUri, final long expectedContactId,
    390                 final String expectedEncodedLookup) {
    391             mContactsProvider.expectQuery(sourceUri)
    392                     .withProjection(Contacts.LOOKUP_KEY, Contacts._ID)
    393                     .returnRow(expectedEncodedLookup, expectedContactId);
    394         }
    395 
    396         void fetchContactIdAndLookupFromRawContactUri(final Uri rawContactUri,
    397                 final long expectedContactId, final String expectedEncodedLookup) {
    398             // TODO: use a lighter query by joining rawcontacts with contacts in provider
    399             // (See ContactContracts.java)
    400             final Uri dataUri = Uri.withAppendedPath(rawContactUri,
    401                     RawContacts.Data.CONTENT_DIRECTORY);
    402             mContactsProvider.expectQuery(dataUri)
    403                     .withProjection(RawContacts.CONTACT_ID, Contacts.LOOKUP_KEY)
    404                     .returnRow(expectedContactId, expectedEncodedLookup);
    405         }
    406     }
    407 }
    408