Home | History | Annotate | Download | only in dialpad
      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         SmartDialPrefix.setUserInNanpRegion(true);
    202         // An 11 digit number prefixed with 1 should be matched by the 10 digit number, as well as
    203         // the 7 digit number (without area code)
    204         checkMatchesNumber("1-510-333-7596", "5103337596", true, true, 2, 14);
    205         checkMatchesNumber("1-510-333-7596", "3337596", true, true, 6, 14);
    206 
    207         // An 11 digit number prefixed with +1 should be matched by the 10 digit number, as well as
    208         // the 7 digit number (without area code)
    209         checkMatchesNumber("+1-510-333-7596", "5103337596", true, true, 3, 15);
    210         checkMatchesNumber("+1-510-333-7596", "3337596", true, true, 7, 15);
    211         checkMatchesNumber("+1-510-333-7596", "103337596", false, true, 0, 0);
    212         checkMatchesNumber("+1-510-333-7596", "337596", false, true, 0, 0);
    213         checkMatchesNumber("+1510 3337596", "5103337596", true, true, 2, 13);
    214         checkMatchesNumber("+1510 3337596", "3337596", true, true, 6, 13);
    215         checkMatchesNumber("+1510 3337596", "103337596", false, true, 0, 0);
    216         checkMatchesNumber("+1510 3337596", "37596", false, true, 0, 0);
    217 
    218         // Invalid NANP numbers should not be matched
    219         checkMatchesNumber("1-510-333-759", "510333759", false, true, 0, 0);
    220         checkMatchesNumber("510-333-759", "333759", false, true, 0, 0);
    221 
    222         // match should fail if NANP flag is switched off
    223         checkMatchesNumber("1-510-333-7596", "3337596", false, false, 0, 0);
    224 
    225         // A 10 digit number without a 1 prefix should be matched by the 7 digit number
    226         checkMatchesNumber("(650) 292 2323", "2922323", true, true, 6, 14);
    227         checkMatchesNumber("(650) 292 2323", "6502922323", true, true, 0, 14);
    228         // match should fail if NANP flag is switched off
    229         checkMatchesNumber("(650) 292 2323", "2922323", false, false, 0, 0);
    230         // But this should still match (since it is the full number)
    231         checkMatchesNumber("(650) 292 2323", "6502922323", true, false, 0, 14);
    232     }
    233 
    234 
    235     private void checkMatchesNumber(String number, String query, boolean expectedMatches,
    236             int matchStart, int matchEnd) {
    237         checkMatchesNumber(number, query, expectedMatches, false, matchStart, matchEnd);
    238     }
    239 
    240     private void checkMatchesNumber(String number, String query, boolean expectedMatches,
    241             boolean matchNanp, int matchStart, int matchEnd) {
    242         final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query);
    243         final SmartDialMatchPosition pos = matcher.matchesNumber(number, query, matchNanp);
    244         assertEquals(expectedMatches, pos != null);
    245         if (expectedMatches) {
    246             assertEquals("start", matchStart, pos.start);
    247             assertEquals("end", matchEnd, pos.end);
    248         }
    249     }
    250 
    251     private void checkMatches(String displayName, String query, boolean expectedMatches,
    252             int... expectedMatchPositions) {
    253         final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query);
    254         final ArrayList<SmartDialMatchPosition> matchPositions =
    255                 new ArrayList<SmartDialMatchPosition>();
    256         final boolean matches = matcher.matchesCombination(
    257                 displayName, query, matchPositions);
    258         Log.d(TAG, "query=" + query + "  text=" + displayName
    259                 + "  nfd=" + Normalizer.normalize(displayName, Normalizer.Form.NFD)
    260                 + "  nfc=" + Normalizer.normalize(displayName, Normalizer.Form.NFC)
    261                 + "  nfkd=" + Normalizer.normalize(displayName, Normalizer.Form.NFKD)
    262                 + "  nfkc=" + Normalizer.normalize(displayName, Normalizer.Form.NFKC)
    263                 + "  matches=" + matches);
    264         assertEquals("matches", expectedMatches, matches);
    265         final int length = expectedMatchPositions.length;
    266         assertEquals(length % 2, 0);
    267         if (matches) {
    268             for (int i = 0; i < length/2; i++) {
    269                 assertEquals("start", expectedMatchPositions[i * 2], matchPositions.get(i).start);
    270                 assertEquals("end", expectedMatchPositions[i * 2 + 1], matchPositions.get(i).end);
    271             }
    272         }
    273     }
    274 
    275 }
    276