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