summaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/providers/contacts/GlobalSearchSupportTest.java
blob: 803628915ef89c00b349603730ed127e15bc6482 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.providers.contacts;

import android.accounts.Account;
import android.app.SearchManager;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.StatusUpdates;
import android.test.suitebuilder.annotation.MediumTest;

import com.android.providers.contacts.testutil.DataUtil;
import com.android.providers.contacts.testutil.RawContactUtil;

/**
 * Unit tests for {@link GlobalSearchSupport}.
 * <p>
 * Run the test like this:
 * <p>
 * <code><pre>
 * adb shell am instrument -e class com.android.providers.contacts.GlobalSearchSupportTest -w \
 *         com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
 * </pre></code>
 */
@MediumTest
public class GlobalSearchSupportTest extends BaseContactsProvider2Test {

    public void testSearchSuggestionsNotInDefaultDirectory() throws Exception {
        Account account = new Account("actname", "acttype");

        // Creating an AUTO_ADD group will exclude all ungrouped contacts from global search
        createGroup(account, "any", "any", 0 /* visible */, true /* auto-add */, false /* fav */);

        long rawContactId = RawContactUtil.createRawContact(mResolver, account);
        DataUtil.insertStructuredName(mResolver, rawContactId, "Deer", "Dough");

        // Remove the new contact from all groups
        mResolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
                + " AND " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE + "'", null);

        Uri searchUri = new Uri.Builder().scheme("content").authority(ContactsContract.AUTHORITY)
                .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath("D").build();

        // If the contact is not in the "my contacts" group, nothing should be found
        Cursor c = mResolver.query(searchUri, null, null, null, null);
        assertEquals(0, c.getCount());
        c.close();
    }

    public void testSearchSuggestionsByNameWithPhoto() throws Exception {
        GoldenContact contact = new GoldenContactBuilder().name("Deer", "Dough").photo(
                loadTestPhoto()).build();
        new SuggestionTesterBuilder(contact).query("D").expectIcon1Uri(true).expectedText1(
                "Deer Dough").build().test();
    }

    public void testSearchSuggestionsByEmailWithPhoto() {
        GoldenContact contact = new GoldenContactBuilder().name("Deer", "Dough").photo(
                loadTestPhoto()).email("foo@acme.com").build();
        new SuggestionTesterBuilder(contact).query("foo@ac").expectIcon1Uri(true).expectedIcon2(
                String.valueOf(StatusUpdates.getPresenceIconResourceId(StatusUpdates.OFFLINE)))
                .expectedText1("Deer Dough").expectedText2("foo@acme.com").build().test();
    }

    public void testSearchSuggestionsByName() {
        GoldenContact contact = new GoldenContactBuilder().name("Deer", "Dough").company("Google")
                .build();
        new SuggestionTesterBuilder(contact).query("D").expectedText1("Deer Dough").expectedText2(
                null).build().test();
    }

    public void testSearchByNickname() {
        GoldenContact contact = new GoldenContactBuilder().name("Deer", "Dough").nickname(
                "Little Fawn").company("Google").build();
        new SuggestionTesterBuilder(contact).query("L").expectedText1("Deer Dough").expectedText2(
                "Little Fawn").build().test();
    }

    public void testSearchByCompany() {
        GoldenContact contact = new GoldenContactBuilder().name("Deer", "Dough").company("Google")
                .build();
        new SuggestionTesterBuilder(contact).query("G").expectedText1("Deer Dough").expectedText2(
                "Google").build().test();
    }

    public void testSearchByTitleWithCompany() {
        GoldenContact contact = new GoldenContactBuilder().name("Deer", "Dough").company("Google")
                .title("Software Engineer").build();
        new SuggestionTesterBuilder(contact).query("S").expectIcon1Uri(false).expectedText1(
                "Deer Dough").expectedText2("Software Engineer, Google").build().test();
    }

    public void testSearchSuggestionsByPhoneNumberOnNonPhone() throws Exception {
        getContactsProvider().setIsPhone(false);

        GoldenContact contact = new GoldenContactBuilder().name("Deer", "Dough").photo(
                loadTestPhoto()).phone("1-800-4664-411").build();
        new SuggestionTesterBuilder(contact).query("1800").expectIcon1Uri(true).expectedText1(
                "Deer Dough").expectedText2("1-800-4664-411").build().test();
    }

    /**
     * Tests that the quick search suggestion returns the expected contact
     * information.
     */
    private final class SuggestionTester {

        private final GoldenContact contact;

        private final String query;

        private final boolean expectIcon1Uri;

        private final String expectedIcon2;

        private final String expectedText1;

        private final String expectedText2;

        public SuggestionTester(SuggestionTesterBuilder builder) {
            contact = builder.contact;
            query = builder.query;
            expectIcon1Uri = builder.expectIcon1Uri;
            expectedIcon2 = builder.expectedIcon2;
            expectedText1 = builder.expectedText1;
            expectedText2 = builder.expectedText2;
        }

        /**
         * Tests suggest and refresh queries from quick search box, then deletes the contact from
         * the data base.
         */
        public void test() {

            testQsbSuggest();
            testContactIdQsbRefresh();
            testLookupKeyQsbRefresh();

            // Cleanup
            contact.delete();
        }

        /**
         * Tests that the contacts provider return the appropriate information from the golden
         * contact in response to the suggestion query from the quick search box.
         */
        private void testQsbSuggest() {

            Uri searchUri = new Uri.Builder().scheme("content").authority(
                    ContactsContract.AUTHORITY).appendPath(SearchManager.SUGGEST_URI_PATH_QUERY)
                    .appendPath(query).build();

            Cursor c = mResolver.query(searchUri, null, null, null, null);
            assertEquals(1, c.getCount());
            c.moveToFirst();

            String icon1 = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1));
            if (expectIcon1Uri) {
                assertTrue(icon1.startsWith("content:"));
            } else {
                assertEquals(String.valueOf(com.android.internal.R.drawable.ic_contact_picture),
                        icon1);
            }

            // SearchManager does not declare a constant for _id
            ContentValues values = getContactValues();
            assertCursorValues(c, values);

            c.close();
        }

        /**
         * Returns the expected Quick Search Box content values for the golden contact.
         */
        private ContentValues getContactValues() {

            ContentValues values = new ContentValues();
            values.put("_id", contact.getContactId());
            values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, expectedText1);
            values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, expectedText2);

            values.put(SearchManager.SUGGEST_COLUMN_ICON_2, expectedIcon2);
            values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA,
                    Contacts.getLookupUri(contact.getContactId(), contact.getLookupKey())
                            .toString());
            values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, contact.getLookupKey());
            values.put(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA, query);
            return values;
        }

        /**
         * Returns the expected Quick Search Box content values for the golden contact.
         */
        private ContentValues getRefreshValues() {

            ContentValues values = new ContentValues();
            values.put("_id", contact.getContactId());
            values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, expectedText1);
            values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, expectedText2);

            values.put(SearchManager.SUGGEST_COLUMN_ICON_2, expectedIcon2);
            values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, contact.getLookupKey());
            values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, contact.getLookupKey());
            return values;
        }

        /**
         * Performs the refresh query and returns a cursor to the results.
         *
         * @param refreshId the final component path of the refresh query, which identifies which
         *        contact to refresh.
         */
        private Cursor refreshQuery(String refreshId) {

            // See if the same result is returned by a shortcut refresh
            Uri refershUri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath(
                    SearchManager.SUGGEST_URI_PATH_SHORTCUT)
                    .appendPath(refreshId)
                    .appendQueryParameter(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA, query)
                    .build();

            String[] projection = new String[] {
                    SearchManager.SUGGEST_COLUMN_ICON_1, SearchManager.SUGGEST_COLUMN_ICON_2,
                    SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_TEXT_2,
                    SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID,
                    SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "_id",
            };

            return mResolver.query(refershUri, projection, null, null, null);
        }

        /**
         * Tests that the contacts provider returns an empty result in response to a refresh query
         * from the quick search box that uses the contact id to identify the contact.  The empty
         * result indicates that the shortcut is no longer valid, and the QSB will replace it with
         * a new-style shortcut the next time they click on the contact.
         *
         * @see #testLookupKeyQsbRefresh()
         */
        private void testContactIdQsbRefresh() {

            Cursor c = refreshQuery(String.valueOf(contact.getContactId()));
            try {
                assertEquals("Record count", 0, c.getCount());
            } finally {
                c.close();
            }
        }

        /**
         * Tests that the contacts provider return the appropriate information from the golden
         * contact in response to the refresh query from the quick search box.  The refresh query
         * uses the currently-supported mechanism of identifying the contact by the lookup key,
         * which is more stable than the previously used contact id.
         */
        private void testLookupKeyQsbRefresh() {

            Cursor c = refreshQuery(contact.getLookupKey());
            try {
                assertEquals("Record count", 1, c.getCount());
                c.moveToFirst();
                assertCursorValues(c, getRefreshValues());
            } finally {
                c.close();
            }
        }
    }

    /**
     * Builds {@link SuggestionTester} objects. Unspecified boolean objects default to
     * false. Unspecified String objects default to null.
     */
    private final class SuggestionTesterBuilder {

        private final GoldenContact contact;

        private String query;

        private boolean expectIcon1Uri;

        private String expectedIcon2;

        private String expectedText1;

        private String expectedText2;

        public SuggestionTesterBuilder(GoldenContact contact) {
            this.contact = contact;
        }

        /**
         * Builds the {@link SuggestionTester} specified by this builder.
         */
        public SuggestionTester build() {
            return new SuggestionTester(this);
        }

        /**
         * The text of the user's query to quick search (i.e., what they typed
         * in the search box).
         */
        public SuggestionTesterBuilder query(String value) {
            query = value;
            return this;
        }

        /**
         * Whether to set Icon1, which in practice is the contact's photo.
         * <p>
         * TODO(tomo): Replace with actual expected value? This might be hard
         * because the values look non-deterministic, such as
         * "content://com.android.contacts/contacts/2015/photo"
         */
        public SuggestionTesterBuilder expectIcon1Uri(boolean value) {
            expectIcon1Uri = value;
            return this;
        }

        /**
         * The value for Icon2, which in practice is the contact's Chat status
         * (available, busy, etc.)
         */
        public SuggestionTesterBuilder expectedIcon2(String value) {
            expectedIcon2 = value;
            return this;
        }

        /**
         * First line of suggestion text expected to be returned (required).
         */
        public SuggestionTesterBuilder expectedText1(String value) {
            expectedText1 = value;
            return this;
        }

        /**
         * Second line of suggestion text expected to return (optional).
         */
        public SuggestionTesterBuilder expectedText2(String value) {
            expectedText2 = value;
            return this;
        }
    }
}