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