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.providers.contacts;
18
19import android.content.ContentUris;
20import android.net.Uri;
21import android.provider.ContactsContract.AggregationExceptions;
22import android.provider.ContactsContract.Contacts;
23import android.provider.ContactsContract.RawContacts;
24import android.test.suitebuilder.annotation.MediumTest;
25
26import com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
27
28import java.util.ArrayList;
29
30/**
31 * Unit tests for {@link ContactLookupKey}.
32 *
33 * Run the test like this:
34 * <code>
35 * adb shell am instrument -e class com.android.providers.contacts.ContactLookupKeyTest -w \
36 *         com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
37 * </code>
38 */
39@MediumTest
40public class ContactLookupKeyTest extends BaseContactsProvider2Test {
41
42    public void testLookupKeyUsingDisplayNameAndNoAccount() {
43        long rawContactId1 = createRawContactWithName("John", "Doe");
44        long rawContactId2 = createRawContactWithName("johndoe", null);
45        setAggregationException(
46                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
47
48        // Normalized display name
49        String normalizedName = NameNormalizer.normalize("johndoe");
50        String expectedLookupKey = "0r" + rawContactId1 + "-" + normalizedName + ".0r"
51                + rawContactId2 + "-" + normalizedName;
52
53        long contactId = queryContactId(rawContactId1);
54        assertStoredValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
55                Contacts.LOOKUP_KEY, expectedLookupKey);
56
57        // Find the contact using lookup key by itself
58        Uri lookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, expectedLookupKey);
59        assertStoredValue(lookupUri, Contacts._ID, contactId);
60
61        // Find the contact using both the lookup key and the correct contact ID
62        assertStoredValue(ContentUris.withAppendedId(lookupUri, contactId),
63                Contacts._ID, contactId);
64
65        // Find the contact using both the lookup key and an incorrect contact ID
66        assertStoredValue(ContentUris.withAppendedId(lookupUri, contactId + 1),
67                Contacts._ID, contactId);
68    }
69
70    public void testLookupKeyUsingSourceIdAndNoAccount() {
71        long rawContactId1 = createRawContactWithName("John", "Doe");
72        storeValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.SOURCE_ID, "123");
73
74        long rawContactId2 = createRawContactWithName("johndoe", null);
75        storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.SOURCE_ID, "4.5.6");
76
77        long rawContactId3 = createRawContactWithName("john", "dough");
78        storeValue(RawContacts.CONTENT_URI, rawContactId3, RawContacts.SOURCE_ID, "http://foo?bar");
79
80        setAggregationException(
81                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
82        setAggregationException(
83                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId3);
84
85        // Two source ids, of them escaped
86        String expectedLookupKey = "0i123.0e4..5..6.0ihttp%3A%2F%2Ffoo%3Fbar";
87
88        long contactId = queryContactId(rawContactId1);
89        assertStoredValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
90                Contacts.LOOKUP_KEY, expectedLookupKey);
91
92        Uri lookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, expectedLookupKey);
93        assertStoredValue(lookupUri, Contacts._ID, contactId);
94    }
95
96    public void testLookupKeySameSourceIdDifferentAccounts() {
97        long rawContactId1 = createRawContactWithName("Dear", "Doe");
98        storeValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.ACCOUNT_TYPE, "foo");
99        storeValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.ACCOUNT_NAME, "FOO");
100        storeValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.SOURCE_ID, "1");
101
102        long rawContactId2 = createRawContactWithName("Deer", "Dough");
103        storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.ACCOUNT_TYPE, "bar");
104        storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.ACCOUNT_NAME, "BAR");
105        storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.SOURCE_ID, "1");
106
107        assertNotAggregated(rawContactId1, rawContactId2);
108
109        int accountHashCode1 = ContactLookupKey.getAccountHashCode("foo", "FOO");
110        int accountHashCode2 = ContactLookupKey.getAccountHashCode("bar", "BAR");
111
112        long contactId1 = queryContactId(rawContactId1);
113        assertStoredValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1),
114                Contacts.LOOKUP_KEY, accountHashCode1 + "i1");
115
116        long contactId2 = queryContactId(rawContactId2);
117        assertStoredValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2),
118                Contacts.LOOKUP_KEY, accountHashCode2 + "i1");
119
120        Uri lookupUri1 = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, accountHashCode1 + "i1");
121        assertStoredValue(lookupUri1, Contacts._ID, contactId1);
122
123        Uri lookupUri2 = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, accountHashCode2 + "i1");
124        assertStoredValue(lookupUri2, Contacts._ID, contactId2);
125    }
126
127    public void testLookupKeyChoosingLargestContact() {
128        long rawContactId1 = createRawContactWithName("John", "Doe");
129        storeValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.SOURCE_ID, "1");
130
131        long rawContactId2 = createRawContactWithName("John", "Doe");
132        storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.SOURCE_ID, "2");
133
134        long rawContactId3 = createRawContactWithName("John", "Doe");
135        storeValue(RawContacts.CONTENT_URI, rawContactId3, RawContacts.SOURCE_ID, "3");
136        setAggregationException(
137                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
138        setAggregationException(
139                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId3);
140
141        String lookupKey = "0i1.0i2.0i3";
142
143        long contactId = queryContactId(rawContactId1);
144        assertStoredValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
145                Contacts.LOOKUP_KEY, lookupKey);
146
147        setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, rawContactId1,
148                rawContactId3);
149        setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, rawContactId2,
150                rawContactId3);
151        assertAggregated(rawContactId1, rawContactId2);
152        assertNotAggregated(rawContactId1, rawContactId3);
153        assertNotAggregated(rawContactId2, rawContactId3);
154
155        long largerContactId = queryContactId(rawContactId1);
156        assertStoredValue(
157                ContentUris.withAppendedId(Contacts.CONTENT_URI, largerContactId),
158                Contacts.LOOKUP_KEY, "0i1.0i2");
159        assertStoredValue(
160                ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId3)),
161                Contacts.LOOKUP_KEY, "0i3");
162
163        Uri lookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
164        assertStoredValue(lookupUri, Contacts._ID, largerContactId);
165    }
166
167    public void testGetLookupUri() {
168        long rawContactId1 = createRawContactWithName("John", "Doe");
169        storeValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.SOURCE_ID, "1");
170
171        long contactId = queryContactId(rawContactId1);
172        String lookupUri = "content://com.android.contacts/contacts/lookup/0i1/" + contactId;
173
174        Uri contentUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
175        assertEquals(lookupUri,
176                Contacts.getLookupUri(mResolver, contentUri).toString());
177
178        Uri staleLookupUri = ContentUris.withAppendedId(
179                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, "0i1"),
180                contactId+2);
181        assertEquals(lookupUri,
182                Contacts.getLookupUri(mResolver, staleLookupUri).toString());
183    }
184
185    public void testParseLookupKey() {
186        // Display name
187        assertLookupKey("123n1248AC",
188                new int[]{123},
189                new int[]{ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME},
190                new String[]{"1248AC"});
191
192        // Raw contact ID + display name
193        assertLookupKey("123r20-1248AC",
194                new int[]{123},
195                new int[]{ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID},
196                new String[]{"1248AC"});
197
198        // Unescaped source ID
199        assertLookupKey("0i1248AC-X",
200                new int[]{0},
201                new int[]{ContactLookupKey.LOOKUP_TYPE_SOURCE_ID},
202                new String[]{"1248AC-X"});
203
204        // Escaped source ID
205        assertLookupKey("432e12..48AC",
206                new int[]{432},
207                new int[]{ContactLookupKey.LOOKUP_TYPE_SOURCE_ID},
208                new String[]{"12.48AC"});
209
210        // Compound lookup key
211        assertLookupKey("123n1248AC.0i1248AC.432e12..48AC.123n1248AC.123r30-2184CA",
212                new int[]{123, 0, 432, 123},
213                new int[] {
214                        ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME,
215                        ContactLookupKey.LOOKUP_TYPE_SOURCE_ID,
216                        ContactLookupKey.LOOKUP_TYPE_SOURCE_ID,
217                        ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME,
218                        ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID,
219                },
220                new String[]{"1248AC", "1248AC", "12.48AC", "1248AC", "2184CA"});
221    }
222
223    private void assertLookupKey(String lookupKey, int[] accountHashCodes, int[] types,
224            String[] keys) {
225        ContactLookupKey key = new ContactLookupKey();
226        ArrayList<LookupKeySegment> list = key.parse(lookupKey);
227        assertEquals(types.length, list.size());
228
229        for (int i = 0; i < accountHashCodes.length; i++) {
230            LookupKeySegment segment = list.get(i);
231            assertEquals(accountHashCodes[i], segment.accountHashCode);
232            assertEquals(types[i], segment.lookupType);
233            assertEquals(keys[i], segment.key);
234        }
235    }
236}
237
238