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