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.ContentValues; 20import android.net.Uri; 21import android.net.Uri.Builder; 22import android.provider.ContactsContract.CommonDataKinds.Im; 23import android.provider.ContactsContract.CommonDataKinds.Organization; 24import android.provider.ContactsContract.CommonDataKinds.StructuredName; 25import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 26import android.provider.ContactsContract.Contacts; 27import android.provider.ContactsContract.SearchSnippets; 28import android.test.MoreAsserts; 29import android.test.suitebuilder.annotation.MediumTest; 30import android.test.suitebuilder.annotation.Suppress; 31 32import com.android.providers.contacts.testutil.DataUtil; 33import com.android.providers.contacts.testutil.RawContactUtil; 34 35import java.text.Collator; 36import java.util.Arrays; 37import java.util.Locale; 38 39/** 40 * Unit tests for {@link SearchIndexManager}. 41 * 42 * Run the test like this: 43 * <code> 44 * adb shell am instrument -e class com.android.providers.contacts.SearchIndexManagerTest -w \ 45 * com.android.providers.contacts.tests/android.test.InstrumentationTestRunner 46 * </code> 47 */ 48@MediumTest 49public class SearchIndexManagerTest extends BaseContactsProvider2Test { 50 51 public void testSearchIndexForStructuredName() { 52 long rawContactId = RawContactUtil.createRawContact(mResolver); 53 long contactId = queryContactId(rawContactId); 54 DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe"); 55 ContentValues values = new ContentValues(); 56 values.put(StructuredName.DISPLAY_NAME, "Bob I. Parr"); 57 DataUtil.insertStructuredName(mResolver, rawContactId, values); 58 values.clear(); 59 values.put(StructuredName.PREFIX, "Mrs."); 60 values.put(StructuredName.GIVEN_NAME, "Helen"); 61 values.put(StructuredName.MIDDLE_NAME, "I."); 62 values.put(StructuredName.FAMILY_NAME, "Parr"); 63 values.put(StructuredName.SUFFIX, "PhD"); 64 values.put(StructuredName.PHONETIC_FAMILY_NAME, "par"); 65 values.put(StructuredName.PHONETIC_GIVEN_NAME, "helen"); 66 DataUtil.insertStructuredName(mResolver, rawContactId, values); 67 68 assertSearchIndex( 69 contactId, null, "John Doe Bob I Parr Helen I Parr PhD par helen parhelen", null); 70 } 71 72 public void testSearchIndexForStructuredName_phoneticOnly() { 73 long rawContactId = RawContactUtil.createRawContact(mResolver); 74 long contactId = queryContactId(rawContactId); 75 DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe"); 76 ContentValues values = new ContentValues(); 77 values.put(StructuredName.DISPLAY_NAME, "Bob I. Parr"); 78 DataUtil.insertStructuredName(mResolver, rawContactId, values); 79 values.clear(); 80 values.put(StructuredName.PREFIX, "Mrs."); 81 values.put(StructuredName.GIVEN_NAME, "Helen"); 82 values.put(StructuredName.MIDDLE_NAME, "I."); 83 values.put(StructuredName.FAMILY_NAME, "Parr"); 84 values.put(StructuredName.SUFFIX, "PhD"); 85 values.put(StructuredName.PHONETIC_FAMILY_NAME, "yamada"); 86 values.put(StructuredName.PHONETIC_GIVEN_NAME, "taro"); 87 DataUtil.insertStructuredName(mResolver, rawContactId, values); 88 89 assertSearchIndex(contactId, null, "yamada taro", null); 90 } 91 92 public void testSearchIndexForChineseName() { 93 // Only run this test when Chinese collation is supported 94 if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) { 95 return; 96 } 97 98 long rawContactId = RawContactUtil.createRawContact(mResolver); 99 long contactId = queryContactId(rawContactId); 100 ContentValues values = new ContentValues(); 101 values.put(StructuredName.DISPLAY_NAME, "\u695A\u8FAD"); // CHUCI 102 DataUtil.insertStructuredName(mResolver, rawContactId, values); 103 104 assertSearchIndex( 105 contactId, null, "\u695A\u8FAD \u695A\u8FAD CI \u8FAD CHUCI CC C", null); 106 } 107 108 public void testSearchByChineseName() { 109 // Only run this test when Chinese collation is supported 110 if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) { 111 return; 112 } 113 ContactLocaleUtils.setLocaleForTest(Locale.SIMPLIFIED_CHINESE); 114 115 long rawContactId = RawContactUtil.createRawContact(mResolver); 116 ContentValues values = new ContentValues(); 117 values.put(StructuredName.DISPLAY_NAME, "\u695A\u8FAD"); // CHUCI 118 DataUtil.insertStructuredName(mResolver, rawContactId, values); 119 120 assertStoredValue(buildSearchUri("\u695A\u8FAD"), SearchSnippets.SNIPPET, null); 121 assertStoredValue(buildSearchUri("\u8FAD"), SearchSnippets.SNIPPET, null); 122 assertStoredValue(buildSearchUri("CI"), SearchSnippets.SNIPPET, null); 123 assertStoredValue(buildSearchUri("CHUCI"), SearchSnippets.SNIPPET, null); 124 assertStoredValue(buildSearchUri("CC"), SearchSnippets.SNIPPET, null); 125 assertStoredValue(buildSearchUri("C"), SearchSnippets.SNIPPET, null); 126 } 127 128 public void testSearchIndexForKoreanName() { 129 // Only run this test when Korean collation is supported 130 if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.KOREA)) { 131 return; 132 } 133 134 long rawContactId = RawContactUtil.createRawContact(mResolver); 135 long contactId = queryContactId(rawContactId); 136 ContentValues values = new ContentValues(); 137 values.put(StructuredName.DISPLAY_NAME, "\uC774\uC0C1\uC77C"); // Lee Sang Il 138 DataUtil.insertStructuredName(mResolver, rawContactId, values); 139 140 assertSearchIndex(contactId, null, 141 "\uC774\uC0C1\uC77C \uC0C1\uC77C \u1109\u110B \u110B\u1109\u110B", null); 142 } 143 144 public void testSearchByKoreanName() { 145 // Only run this test when Korean collation is supported 146 if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.KOREA)) { 147 return; 148 } 149 150 long rawContactId = RawContactUtil.createRawContact(mResolver); 151 ContentValues values = new ContentValues(); 152 values.put(StructuredName.DISPLAY_NAME, "\uC774\uC0C1\uC77C"); // Lee Sang Il 153 DataUtil.insertStructuredName(mResolver, rawContactId, values); 154 155 // Full name: Lee Sang Il 156 assertStoredValue(buildSearchUri("\uC774\uC0C1\uC77C"), SearchSnippets.SNIPPET, null); 157 158 // Given name: Sang Il 159 assertStoredValue(buildSearchUri("\uC0C1\uC77C"), SearchSnippets.SNIPPET, null); 160 161 // Consonants of given name: SIOS IEUNG 162 assertStoredValue(buildSearchUri("\u1109\u110B"), SearchSnippets.SNIPPET, null); 163 164 // Consonants of full name: RIEUL SIOS IEUNG 165 assertStoredValue(buildSearchUri("\u110B\u1109\u110B"), SearchSnippets.SNIPPET, null); 166 } 167 168 public void testSearchByKoreanNameWithTwoCharactersFamilyName() { 169 // Only run this test when Korean collation is supported. 170 if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.KOREA)) { 171 return; 172 } 173 174 long rawContactId = RawContactUtil.createRawContact(mResolver); 175 176 // Sun Woo Young Nyeu 177 ContentValues values = new ContentValues(); 178 values.put(StructuredName.DISPLAY_NAME, "\uC120\uC6B0\uC6A9\uB140"); 179 180 DataUtil.insertStructuredName(mResolver, rawContactId, values); 181 182 // Full name: Sun Woo Young Nyeu 183 assertStoredValue( 184 buildSearchUri("\uC120\uC6B0\uC6A9\uB140"), SearchSnippets.SNIPPET, null); 185 186 // Given name: Young Nyeu 187 assertStoredValue(buildSearchUri("\uC6A9\uB140"), SearchSnippets.SNIPPET, null); 188 189 // Consonants of given name: IEUNG NIEUN 190 assertStoredValue(buildSearchUri("\u110B\u1102"), SearchSnippets.SNIPPET, null); 191 192 // Consonants of full name: SIOS IEUNG IEUNG NIEUN 193 assertStoredValue( 194 buildSearchUri("\u1109\u110B\u110B\u1102"), SearchSnippets.SNIPPET, null); 195 } 196 197 public void testSearchIndexForOrganization() { 198 long rawContactId = RawContactUtil.createRawContact(mResolver); 199 long contactId = queryContactId(rawContactId); 200 ContentValues values = new ContentValues(); 201 values.put(Organization.COMPANY, "Acme Inc."); 202 values.put(Organization.TITLE, "Director"); 203 values.put(Organization.DEPARTMENT, "Phones and tablets"); 204 values.put(Organization.JOB_DESCRIPTION, "full text search"); 205 values.put(Organization.SYMBOL, "ACME"); 206 values.put(Organization.PHONETIC_NAME, "ack-me"); 207 values.put(Organization.OFFICE_LOCATION, "virtual"); 208 insertOrganization(rawContactId, values); 209 210 assertSearchIndex(contactId, 211 "Director, Acme Inc. (ack-me) (ACME)/Phones and tablets/virtual/full text search", 212 null, null); 213 } 214 215 public void testSearchIndexForPhoneNumber() { 216 long rawContactId = RawContactUtil.createRawContact(mResolver); 217 long contactId = queryContactId(rawContactId); 218 insertPhoneNumber(rawContactId, "800555GOOG"); 219 insertPhoneNumber(rawContactId, "8005551234"); 220 221 assertSearchIndex(contactId, null, null, "8005554664 +18005554664 8005551234 +18005551234"); 222 } 223 224 public void testSearchIndexForEmail() { 225 long rawContactId = RawContactUtil.createRawContact(mResolver); 226 long contactId = queryContactId(rawContactId); 227 insertEmail(rawContactId, "Bob Parr <incredible@android.com>"); 228 insertEmail(rawContactId, "bob_parr@android.com"); 229 230 assertSearchIndex(contactId, "Bob Parr <incredible@android.com>\nbob_parr@android.com", 231 null, null); 232 } 233 234 public void testSearchIndexForNickname() { 235 long rawContactId = RawContactUtil.createRawContact(mResolver); 236 long contactId = queryContactId(rawContactId); 237 insertNickname(rawContactId, "incredible"); 238 239 assertSearchIndex(contactId, "incredible", null, null); 240 } 241 242 public void testSearchIndexForStructuredPostal() { 243 long rawContactId = RawContactUtil.createRawContact(mResolver); 244 long contactId = queryContactId(rawContactId); 245 insertPostalAddress(rawContactId, "1600 Amphitheatre Pkwy\nMountain View, CA 94043"); 246 ContentValues values = new ContentValues(); 247 values.put(StructuredPostal.CITY, "London"); 248 values.put(StructuredPostal.STREET, "76 Buckingham Palace Road"); 249 values.put(StructuredPostal.POSTCODE, "SW1W 9TQ"); 250 values.put(StructuredPostal.COUNTRY, "United Kingdom"); 251 insertPostalAddress(rawContactId, values); 252 253 assertSearchIndex(contactId, "1600 Amphitheatre Pkwy Mountain View, CA 94043\n" 254 + "76 Buckingham Palace Road London SW1W 9TQ United Kingdom", null, null); 255 } 256 257 public void testSearchIndexForIm() { 258 long rawContactId = RawContactUtil.createRawContact(mResolver); 259 long contactId = queryContactId(rawContactId); 260 insertImHandle(rawContactId, Im.PROTOCOL_JABBER, null, "bp@android.com"); 261 insertImHandle(rawContactId, Im.PROTOCOL_CUSTOM, "android_im", "android@android.com"); 262 263 assertSearchIndex( 264 contactId, "Jabber/bp@android.com\nandroid_im/android@android.com", null, null); 265 } 266 267 public void testSearchIndexForNote() { 268 long rawContactId = RawContactUtil.createRawContact(mResolver); 269 long contactId = queryContactId(rawContactId); 270 insertNote(rawContactId, "Please note: three notes or more make up a chord."); 271 272 assertSearchIndex( 273 contactId, "Please note: three notes or more make up a chord.", null, null); 274 } 275 276 public void testSnippetArgs() { 277 long rawContactId = RawContactUtil.createRawContact(mResolver); 278 insertNote(rawContactId, "Please note: three notes or more make up a chord."); 279 280 assertStoredValue( 281 buildSearchUri("thr", "[,],-,2", false), SearchSnippets.SNIPPET, 282 "-note: [three]-"); 283 } 284 285 public void testEmptyFilter() { 286 RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 287 assertEquals(0, getCount(buildSearchUri(""), null, null)); 288 } 289 290 public void testSearchByName() { 291 RawContactUtil.createRawContactWithName(mResolver, "John Jay", "Doe"); 292 293 // We are supposed to find the contact, but return a null snippet 294 assertStoredValue(buildSearchUri("john"), SearchSnippets.SNIPPET, null); 295 assertStoredValue(buildSearchUri("jay"), SearchSnippets.SNIPPET, null); 296 assertStoredValue(buildSearchUri("doe"), SearchSnippets.SNIPPET, null); 297 } 298 299 public void testSearchByPrefixName() { 300 RawContactUtil.createRawContactWithName(mResolver, "John Jay", "Doe"); 301 302 // prefix searches 303 assertStoredValue(buildSearchUri("jo ja"), SearchSnippets.SNIPPET, null); 304 assertStoredValue(buildSearchUri("J D"), SearchSnippets.SNIPPET, null); 305 assertStoredValue(buildSearchUri("Doe, John"), SearchSnippets.SNIPPET, null); 306 } 307 308 public void testGermanUmlautFullameCapitalizationSearch() { 309 RawContactUtil.createRawContactWithName(mResolver, "Matthäus BJÖRN Bünyamin", "Reißer"); 310 311 // make sure we can find those, independent of the capitalization 312 assertStoredValue(buildSearchUri("matthäus"), SearchSnippets.SNIPPET, null); 313 assertStoredValue(buildSearchUri("Matthäus"), SearchSnippets.SNIPPET, null); 314 assertStoredValue(buildSearchUri("MATTHÄUS"), SearchSnippets.SNIPPET, null); 315 316 assertStoredValue(buildSearchUri("björn"), SearchSnippets.SNIPPET, null); 317 assertStoredValue(buildSearchUri("Björn"), SearchSnippets.SNIPPET, null); 318 assertStoredValue(buildSearchUri("BJÖRN"), SearchSnippets.SNIPPET, null); 319 320 assertStoredValue(buildSearchUri("bünyamin"), SearchSnippets.SNIPPET, null); 321 assertStoredValue(buildSearchUri("Bünyamin"), SearchSnippets.SNIPPET, null); 322 assertStoredValue(buildSearchUri("BUNYAMIN"), SearchSnippets.SNIPPET, null); 323 324 // There is no capital version of ß. It is capitalized as double-S instead 325 assertStoredValue(buildSearchUri("Reißer"), SearchSnippets.SNIPPET, null); 326 assertStoredValue(buildSearchUri("Reisser"), SearchSnippets.SNIPPET, null); 327 assertStoredValue(buildSearchUri("REISSER"), SearchSnippets.SNIPPET, null); 328 } 329 330 public void testHangulNameLeadConsonantAsYouTypeSearch() { 331 createRawContactWithDisplayName("홍길동"); 332 // the korean name uses three compound characters. this test makes sure 333 // that the name can be found by typing in only the lead consonant 334 assertStoredValue(buildSearchUri("ㅎ"), SearchSnippets.SNIPPET, null); 335 assertStoredValue(buildSearchUri("ㅎㄱ"), SearchSnippets.SNIPPET, null); 336 assertStoredValue(buildSearchUri("ㅎㄱㄷ"), SearchSnippets.SNIPPET, null); 337 338 // same again, this time only for the first name 339 assertStoredValue(buildSearchUri("ㄱ"), SearchSnippets.SNIPPET, null); 340 assertStoredValue(buildSearchUri("ㄱㄷ"), SearchSnippets.SNIPPET, null); 341 } 342 343 public void testHangulNameFullAsYouTypeSearch() { 344 createRawContactWithDisplayName("홍길동"); 345 346 // the korean name uses three compound characters. this test makes sure 347 // that the name can be found by typing in the full nine letters. the search string 348 // shows the name is being built "as you type" 349 assertStoredValue(buildSearchUri("ㅎ"), SearchSnippets.SNIPPET, null); 350 assertStoredValue(buildSearchUri("호"), SearchSnippets.SNIPPET, null); 351 assertStoredValue(buildSearchUri("홍"), SearchSnippets.SNIPPET, null); 352 assertStoredValue(buildSearchUri("홍ㄱ"), SearchSnippets.SNIPPET, null); 353 assertStoredValue(buildSearchUri("홍기"), SearchSnippets.SNIPPET, null); 354 assertStoredValue(buildSearchUri("홍길"), SearchSnippets.SNIPPET, null); 355 assertStoredValue(buildSearchUri("홍길ㄷ"), SearchSnippets.SNIPPET, null); 356 assertStoredValue(buildSearchUri("홍길도"), SearchSnippets.SNIPPET, null); 357 assertStoredValue(buildSearchUri("홍길동"), SearchSnippets.SNIPPET, null); 358 359 // same again, this time only for the first name 360 assertStoredValue(buildSearchUri("ㄱ"), SearchSnippets.SNIPPET, null); 361 assertStoredValue(buildSearchUri("기"), SearchSnippets.SNIPPET, null); 362 assertStoredValue(buildSearchUri("길"), SearchSnippets.SNIPPET, null); 363 assertStoredValue(buildSearchUri("길ㄷ"), SearchSnippets.SNIPPET, null); 364 assertStoredValue(buildSearchUri("길도"), SearchSnippets.SNIPPET, null); 365 assertStoredValue(buildSearchUri("길동"), SearchSnippets.SNIPPET, null); 366 } 367 368 369 /** Decomposed Hangul is not yet supported. This text is how we would test it */ 370 @Suppress 371 public void testHangulNameDecomposedSearch() { 372 createRawContactWithDisplayName("홍길동"); 373 374 // the korean name uses three compound characters. this test makes sure 375 // that the name can be found by typing each syllable as a single character. 376 // This can be achieved using the Korean IM by pressing ㅎ, space, backspace, ㅗ and so on 377 assertStoredValue(buildSearchUri("ㅎ"), SearchSnippets.SNIPPET, null); 378 assertStoredValue(buildSearchUri("ㅎㅗ"), SearchSnippets.SNIPPET, null); 379 assertStoredValue(buildSearchUri("ㅎㅗㅇ"), SearchSnippets.SNIPPET, null); 380 assertStoredValue(buildSearchUri("ㅎㅗㅇㄱ"), SearchSnippets.SNIPPET, null); 381 assertStoredValue(buildSearchUri("ㅎㅗㅇㄱㅣ"), SearchSnippets.SNIPPET, null); 382 assertStoredValue(buildSearchUri("ㅎㅗㅇㄱㅣㄹ"), SearchSnippets.SNIPPET, null); 383 assertStoredValue(buildSearchUri("ㅎㅗㅇㄱㅣㄹㄷ"), SearchSnippets.SNIPPET, null); 384 assertStoredValue(buildSearchUri("ㅎㅗㅇㄱㅣㄹㄷㅗ"), SearchSnippets.SNIPPET, null); 385 assertStoredValue(buildSearchUri("ㅎㅗㅇㄱㅣㄹㄷㅗㅇ"), SearchSnippets.SNIPPET, null); 386 387 // same again, this time only for the first name 388 assertStoredValue(buildSearchUri("ㄱ"), SearchSnippets.SNIPPET, null); 389 assertStoredValue(buildSearchUri("ㄱㅣ"), SearchSnippets.SNIPPET, null); 390 assertStoredValue(buildSearchUri("ㄱㅣㄹ"), SearchSnippets.SNIPPET, null); 391 assertStoredValue(buildSearchUri("ㄱㅣㄹㄷ"), SearchSnippets.SNIPPET, null); 392 assertStoredValue(buildSearchUri("ㄱㅣㄹㄷㅗ"), SearchSnippets.SNIPPET, null); 393 assertStoredValue(buildSearchUri("ㄱㅣㄹㄷㅗㅇ"), SearchSnippets.SNIPPET, null); 394 } 395 396 public void testNameWithHyphen() { 397 RawContactUtil.createRawContactWithName(mResolver, "First", "Last-name"); 398 399 assertStoredValue(buildSearchUri("First"), SearchSnippets.SNIPPET, null); 400 assertStoredValue(buildSearchUri("Last"), SearchSnippets.SNIPPET, null); 401 assertStoredValue(buildSearchUri("Last-"), SearchSnippets.SNIPPET, null); 402 assertStoredValue(buildSearchUri("Last-n"), SearchSnippets.SNIPPET, null); 403 assertStoredValue(buildSearchUri("Last-name"), SearchSnippets.SNIPPET, null); 404 405 // This will work too. 406 assertStoredValue(buildSearchUri("Lastname"), SearchSnippets.SNIPPET, null); 407 408 // This doesn't have to work, but it does with the current implementation. 409 assertStoredValue(buildSearchUri("name"), SearchSnippets.SNIPPET, null); 410 } 411 412 /** Same as {@link #testNameWithHyphen} except the name has double hyphens. */ 413 public void testNameWithDoubleHyphens() { 414 RawContactUtil.createRawContactWithName(mResolver, "First", "Last--name"); 415 416 assertStoredValue(buildSearchUri("First"), SearchSnippets.SNIPPET, null); 417 assertStoredValue(buildSearchUri("Last"), SearchSnippets.SNIPPET, null); 418 assertStoredValue(buildSearchUri("Last-"), SearchSnippets.SNIPPET, null); 419 assertStoredValue(buildSearchUri("Last-n"), SearchSnippets.SNIPPET, null); 420 assertStoredValue(buildSearchUri("Last-name"), SearchSnippets.SNIPPET, null); 421 422 // This will work too. 423 assertStoredValue(buildSearchUri("Lastname"), SearchSnippets.SNIPPET, null); 424 } 425 426 public void testNameWithPunctuations() { 427 RawContactUtil.createRawContactWithName(mResolver, "First", "O'Neill"); 428 429 assertStoredValue(buildSearchUri("first"), SearchSnippets.SNIPPET, null); 430 assertStoredValue(buildSearchUri("oneill"), SearchSnippets.SNIPPET, null); 431 assertStoredValue(buildSearchUri("o'neill"), SearchSnippets.SNIPPET, null); 432 } 433 434 public void testSearchByEmailAddress() { 435 long rawContactId = RawContactUtil.createRawContact(mResolver); 436 insertPhoneNumber(rawContactId, "1234567890"); 437 insertEmail(rawContactId, "john@doe.com"); 438 insertNote(rawContactId, "a hundred dollar note for doe@john.com and bob parr"); 439 440 assertStoredValue(buildSearchUri("john@d"), SearchSnippets.SNIPPET, 441 "[john@doe.com]"); 442 } 443 444 public void testSearchByPhoneNumber() { 445 long rawContactId = RawContactUtil.createRawContact(mResolver); 446 insertPhoneNumber(rawContactId, "330142685300"); 447 insertPhoneNumber(rawContactId, "(800)GOOG-123"); 448 insertEmail(rawContactId, "john@doe.com"); 449 insertNote(rawContactId, "the eighteenth episode of Seinfeld, 650-253-0000"); 450 451 assertStoredValue(buildSearchUri("33 (0)1 42 68 53 00"), SearchSnippets.SNIPPET, 452 "[330142685300]"); 453 assertStoredValue(buildSearchUri("8004664"), SearchSnippets.SNIPPET, 454 "[(800)GOOG-123]"); 455 assertStoredValue(buildSearchUri("650-2"), SearchSnippets.SNIPPET, 456 "\u2026of Seinfeld, [650]-[253]-0000"); 457 458 // for numbers outside of the real phone field, any order (and prefixing) is allowed 459 assertStoredValue(buildSearchUri("25 650"), SearchSnippets.SNIPPET, 460 "\u2026of Seinfeld, [650]-[253]-0000"); 461 } 462 463 /** 464 * Test case for bug 5904515 465 */ 466 public void testSearchByPhoneNumber_diferSnippetting() { 467 long rawContactId = RawContactUtil.createRawContact(mResolver); 468 insertPhoneNumber(rawContactId, "505-123-4567"); 469 470 // If snippeting is deferred, the returned snippet will not contain any markers. 471 assertStoredValue(buildSearchUri("505", "\u0001,\u0001,\u2026,5", true), 472 SearchSnippets.SNIPPET, "505-123-4567"); 473 } 474 475 /** 476 * Equivalent to {@link #testSearchByPhoneNumber_diferSnippetting} for email addresses 477 */ 478 public void testSearchByEmail_diferSnippetting() { 479 long rawContactId = RawContactUtil.createRawContact(mResolver); 480 insertEmail(rawContactId, "john@doe.com"); 481 482 assertStoredValue(buildSearchUri("john", "\u0001,\u0001,\u2026,5", true), 483 SearchSnippets.SNIPPET, "john@doe.com"); 484 } 485 486 public void testSplitIntoFtsTokens() { 487 checkSplitIntoFtsTokens("a", "a"); 488 checkSplitIntoFtsTokens("a_b c%d-e'f", "a_b", "c", "d", "e", "f"); 489 checkSplitIntoFtsTokens(" ", new String[0]); 490 // There's are all "control" characters, but treated as "letters". 491 // (See http://en.wikipedia.org/wiki/C1_Controls_and_Latin-1_Supplement for what they are) 492 checkSplitIntoFtsTokens("\u0080 \u0081 \u0082", "\u0080", "\u0081", "\u0082"); 493 494 // FFF0 is also a token. 495 checkSplitIntoFtsTokens(" \ufff0 ", "\ufff0"); 496 } 497 498 private void checkSplitIntoFtsTokens(String input, String... expectedTokens) { 499 MoreAsserts.assertEquals(expectedTokens, 500 SearchIndexManager.splitIntoFtsTokens(input).toArray(new String[0])); 501 } 502 503 private Uri buildSearchUri(String filter) { 504 return buildSearchUri(filter, false); 505 } 506 507 private Uri buildSearchUri(String filter, boolean deferredSnippeting) { 508 return buildSearchUri(filter, null, deferredSnippeting); 509 } 510 511 private Uri buildSearchUri(String filter, String args, boolean deferredSnippeting) { 512 Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon().appendPath(filter); 513 if (args != null) { 514 builder.appendQueryParameter(SearchSnippets.SNIPPET_ARGS_PARAM_KEY, args); 515 } 516 if (deferredSnippeting) { 517 builder.appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY, "1"); 518 } 519 return builder.build(); 520 } 521 522 private void createRawContactWithDisplayName(String name) { 523 long rawContactId = RawContactUtil.createRawContact(mResolver); 524 ContentValues values = new ContentValues(); 525 values.put(StructuredName.DISPLAY_NAME, name); 526 DataUtil.insertStructuredName(mResolver, rawContactId, values); 527 } 528 529 // TODO: expectedName must be tested. Many tests in here are quite useless at the moment 530 private void assertSearchIndex( 531 long contactId, String expectedContent, String expectedName, String expectedTokens) { 532 ContactsDatabaseHelper dbHelper = (ContactsDatabaseHelper) getContactsProvider() 533 .getDatabaseHelper(); 534 assertEquals(expectedContent, dbHelper.querySearchIndexContentForTest(contactId)); 535 assertEquals(expectedTokens, dbHelper.querySearchIndexTokensForTest(contactId)); 536 } 537} 538 539