1 /* 2 * Copyright (C) 2012 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 17 package com.android.dialer.dialpad; 18 19 import android.test.suitebuilder.annotation.SmallTest; 20 import android.test.suitebuilder.annotation.Suppress; 21 import android.util.Log; 22 import android.test.AndroidTestCase; 23 24 import com.android.dialer.dialpad.SmartDialNameMatcher; 25 import com.android.dialer.dialpad.SmartDialPrefix; 26 27 import java.text.Normalizer; 28 import java.util.ArrayList; 29 30 import junit.framework.TestCase; 31 32 @SmallTest 33 public class SmartDialNameMatcherTest extends TestCase { 34 private static final String TAG = "SmartDialNameMatcherTest"; 35 36 public void testMatches() { 37 // Test to ensure that all alphabetic characters are covered 38 checkMatches("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 39 "22233344455566677778889999" + "22233344455566677778889999", true, 0, 26 * 2); 40 // Should fail because of a mistyped 2 instead of 9 in the second last character 41 checkMatches("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 42 "22233344455566677778889999" + "22233344455566677778889929", false, 0, 0); 43 44 // Basic name test 45 checkMatches("joe", "5", true, 0, 1); 46 checkMatches("joe", "56", true, 0, 2); 47 checkMatches("joe", "563", true, 0, 3); 48 49 // Matches only word boundary. 50 checkMatches("joe", "63", false, 0, 0); 51 checkMatches("joe oe", "63", true, 4, 6); 52 53 // Test for a match across word boundaries 54 checkMatches("joe oe", "56363", true, 0, 6); 55 } 56 57 public void testMatches_repeatedLetters() { 58 checkMatches("aaaaaaaaaa", "2222222222", true, 0, 10); 59 // Fails because of one extra 2 60 checkMatches("aaaaaaaaaa", "22222222222", false, 0, 0); 61 checkMatches("zzzzzzzzzz zzzzzzzzzz", "99999999999999999999", true, 0, 21); 62 } 63 64 public void testMatches_repeatedSpaces() { 65 checkMatches("William J Smith", "9455426576", true, 0, 17); 66 checkMatches("William J Smith", "576", true, 12, 17); 67 // Fails because we start at non-word boundary 68 checkMatches("William J Smith", "6576", false, 0, 0); 69 } 70 71 72 public void testMatches_Initial() { 73 // wjs matches (W)illiam (J)ohn (S)mith 74 checkMatches("William John Smith", "957", true, 0, 1, 8, 9, 13, 14); 75 // wjsmit matches (W)illiam (J)ohn (Smit)h 76 checkMatches("William John Smith", "957648", true, 0, 1, 8, 9, 13, 17); 77 // wjohn matches (W)illiam (John) Smith 78 checkMatches("William John Smith", "95646", true, 0, 1, 8, 12); 79 // jsmi matches William (J)ohn (Smi)th 80 checkMatches("William John Smith", "5764", true, 8, 9, 13, 16); 81 // make sure multiple spaces don't mess things up 82 checkMatches("William John Smith", "5764", true, 15, 16, 22, 25); 83 } 84 85 public void testMatches_InitialWithSeparator() { 86 // wjs matches (W)illiam (J)ohn (S)mith 87 checkMatches("William John-Smith", "957", true, 0, 1, 8, 9, 13, 14); 88 // wjsmit matches (W)illiam (J)ohn-(OShe)a 89 checkMatches("William John-O'Shea", "956743", true, 0, 1, 8, 9, 13, 18); 90 // wjohn matches (W)illiam-(John) Smith 91 checkMatches("William-John Smith", "95646", true, 0, 1, 8, 12); 92 // jsmi matches William (J)ohn-(Smi)th 93 checkMatches("William John-Smith", "5764", true, 8, 9, 13, 16); 94 // wsmi matches (W)illiam John (Smi)th 95 checkMatches("William John-Smith", "9764", true, 0, 1, 13, 16); 96 // make sure multiple spaces don't mess things up 97 checkMatches("William John---Smith", "5764", true, 15, 16, 22, 25); 98 // match tokens that are located directly after a non-space separator (studio) 99 checkMatches("Berkeley Hair-Studio", "788346", true, 14, 20); 100 // match tokens with same initials 101 checkMatches("H.Harold", "427653", true, 2, 8); 102 // various matching combinations of tokens with similar initials 103 checkMatches("Yo-Yoghurt Land", "964487", true, 3, 9); 104 checkMatches("Yo-Yoghurt Land", "96448785263", true, 3, 15); 105 checkMatches("Yo-Yoghurt Land", "95263", true, 3, 4, 11, 15); 106 checkMatches("Yo-Yoghurt Land", "995263", true, 0, 1, 3, 4, 11, 15); 107 108 checkMatches("ab zz ef", "23", true, 0, 1, 6, 7); 109 } 110 111 public void testMatches_repeatedSeparators() { 112 // Simple match for single token 113 checkMatches("John,,,,,Doe", "5646", true, 0, 4); 114 // Match across tokens 115 checkMatches("John,,,,,Doe", "56463", true, 0, 10); 116 // Match token after chain of separators 117 checkMatches("John,,,,,Doe", "363", true, 9, 12); 118 } 119 120 public void testMatches_LatinMix() { 121 // Latin + Chinese characters 122 checkMatches("LeeWang", "59264", true, 0, 1, 5, 9); 123 // Latin + Japanese characters 124 checkMatches("AbcdEfghIJKL", "222333444555", true, 1, 16); 125 // Latin + Arabic characters 126 checkMatches("Peter James", "752637", true, 0, 1, 15, 20); 127 } 128 129 public void testMatches_umlaut() { 130 checkMatches("", "268268", true, 0, 6); 131 } 132 133 public void testMatches_NumberInName() { 134 // Number used as display name 135 checkMatches("+1-123-456-6789", "1234566789", true, 3, 15); 136 // Mix of numbers and letters 137 checkMatches("3rd Grade Teacher", "373", true, 0, 3); 138 checkMatches("1800 Win A Prize", "1800", true, 0, 4); 139 checkMatches("1800 Win A Prize", "1800946277493", true, 0, 16); 140 checkMatches("1800 Win A Prize", "977493", true, 5, 6, 11, 16); 141 } 142 143 144 // TODO: Great if it was treated as "s" or "ss. Figure out if possible without prefix trie? 145 @Suppress 146 public void testMatches_germanSharpS() { 147 checkMatches("", "s", true, 0, 1); 148 checkMatches("", "ss", true, 0, 1); 149 } 150 151 // TODO: Add this and make it work 152 @Suppress 153 public void testMatches_greek() { 154 // http://en.wikipedia.org/wiki/Greek_alphabet 155 fail("Greek letters aren't supported yet."); 156 } 157 158 // TODO: Add this and make it work 159 @Suppress 160 public void testMatches_cyrillic() { 161 // http://en.wikipedia.org/wiki/Cyrillic_script 162 fail("Cyrillic letters aren't supported yet."); 163 } 164 165 166 public void testMatches_NumberBasic() { 167 // Simple basic examples that start the match from the start of the number 168 checkMatchesNumber("5103337596", "510", true, 0, 3); 169 checkMatchesNumber("5103337596", "511", false, 0, 0); 170 checkMatchesNumber("5103337596", "5103337596", true, 0, 10); 171 checkMatchesNumber("123-456-789", "123456789", true, 0, 11); 172 checkMatchesNumber("123-456-789", "123456788", false, 0, 0); 173 checkMatchesNumber("09999999999", "099", true, 0, 3); 174 } 175 176 public void testMatches_NumberWithCountryCode() { 177 // These matches should ignore the country prefix 178 // USA (+1) 179 checkMatchesNumber("+15103337596", "5103337596", true, 2, 12); 180 checkMatchesNumber("+15103337596", "15103337596", true, 0, 12); 181 182 // Singapore (+65) 183 checkMatchesNumber("+6591776930", "6591", true, 0, 5); 184 checkMatchesNumber("+6591776930", "9177", true, 3, 7); 185 checkMatchesNumber("+6591776930", "5917", false, 3, 7); 186 187 // Hungary (+36) 188 checkMatchesNumber("+3612345678", "361234", true, 0, 7); 189 checkMatchesNumber("+3612345678", "1234", true, 3, 7); 190 191 // Hongkong (+852) 192 checkMatchesNumber("+852 2222 2222", "85222222222", true, 0, 14); 193 checkMatchesNumber("+852 2222 3333", "2222", true, 5, 9); 194 195 // Invalid (+854) 196 checkMatchesNumber("+854 1111 2222", "8541111", true, 0, 9); 197 checkMatchesNumber("+854 1111 2222", "1111", false, 0, 0); 198 } 199 200 public void testMatches_NumberNANP() { 201 // An 11 digit number prefixed with 1 should be matched by the 10 digit number, as well as 202 // the 7 digit number (without area code) 203 checkMatchesNumber("1-510-333-7596", "5103337596", true, true, 2, 14); 204 checkMatchesNumber("1-510-333-7596", "3337596", true, true, 6, 14); 205 206 // An 11 digit number prefixed with +1 should be matched by the 10 digit number, as well as 207 // the 7 digit number (without area code) 208 checkMatchesNumber("+1-510-333-7596", "5103337596", true, true, 3, 15); 209 checkMatchesNumber("+1-510-333-7596", "3337596", true, true, 7, 15); 210 checkMatchesNumber("+1-510-333-7596", "103337596", false, true, 0, 0); 211 checkMatchesNumber("+1-510-333-7596", "337596", false, true, 0, 0); 212 checkMatchesNumber("+1510 3337596", "5103337596", true, true, 2, 13); 213 checkMatchesNumber("+1510 3337596", "3337596", true, true, 6, 13); 214 checkMatchesNumber("+1510 3337596", "103337596", false, true, 0, 0); 215 checkMatchesNumber("+1510 3337596", "37596", false, true, 0, 0); 216 217 // Invalid NANP numbers should not be matched 218 checkMatchesNumber("1-510-333-759", "510333759", false, true, 0, 0); 219 checkMatchesNumber("510-333-759", "333759", false, true, 0, 0); 220 221 // match should fail if NANP flag is switched off 222 checkMatchesNumber("1-510-333-7596", "3337596", false, false, 0, 0); 223 224 // A 10 digit number without a 1 prefix should be matched by the 7 digit number 225 checkMatchesNumber("(650) 292 2323", "2922323", true, true, 6, 14); 226 checkMatchesNumber("(650) 292 2323", "6502922323", true, true, 0, 14); 227 // match should fail if NANP flag is switched off 228 checkMatchesNumber("(650) 292 2323", "2922323", false, false, 0, 0); 229 // But this should still match (since it is the full number) 230 checkMatchesNumber("(650) 292 2323", "6502922323", true, false, 0, 14); 231 } 232 233 234 private void checkMatchesNumber(String number, String query, boolean expectedMatches, 235 int matchStart, int matchEnd) { 236 checkMatchesNumber(number, query, expectedMatches, false, matchStart, matchEnd); 237 } 238 239 private void checkMatchesNumber(String number, String query, boolean expectedMatches, 240 boolean matchNanp, int matchStart, int matchEnd) { 241 final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query); 242 final SmartDialMatchPosition pos = matcher.matchesNumber(number, query, matchNanp); 243 assertEquals(expectedMatches, pos != null); 244 if (expectedMatches) { 245 assertEquals("start", matchStart, pos.start); 246 assertEquals("end", matchEnd, pos.end); 247 } 248 } 249 250 private void checkMatches(String displayName, String query, boolean expectedMatches, 251 int... expectedMatchPositions) { 252 final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query); 253 final ArrayList<SmartDialMatchPosition> matchPositions = 254 new ArrayList<SmartDialMatchPosition>(); 255 final boolean matches = matcher.matchesCombination( 256 displayName, query, matchPositions); 257 Log.d(TAG, "query=" + query + " text=" + displayName 258 + " nfd=" + Normalizer.normalize(displayName, Normalizer.Form.NFD) 259 + " nfc=" + Normalizer.normalize(displayName, Normalizer.Form.NFC) 260 + " nfkd=" + Normalizer.normalize(displayName, Normalizer.Form.NFKD) 261 + " nfkc=" + Normalizer.normalize(displayName, Normalizer.Form.NFKC) 262 + " matches=" + matches); 263 assertEquals("matches", expectedMatches, matches); 264 final int length = expectedMatchPositions.length; 265 assertEquals(length % 2, 0); 266 if (matches) { 267 for (int i = 0; i < length/2; i++) { 268 assertEquals("start", expectedMatchPositions[i * 2], matchPositions.get(i).start); 269 assertEquals("end", expectedMatchPositions[i * 2 + 1], matchPositions.get(i).end); 270 } 271 } 272 } 273 274 } 275