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.provider.ContactsContract.FullNameStyle;
20import android.provider.ContactsContract.PhoneticNameStyle;
21import android.test.suitebuilder.annotation.SmallTest;
22
23import com.android.providers.contacts.NameSplitter.Name;
24
25import junit.framework.TestCase;
26
27import java.util.Locale;
28
29/**
30 * Tests for {@link NameSplitter}.
31 *
32 * Run the test like this:
33 * <code>
34 * adb shell am instrument -e class com.android.providers.contacts.NameSplitterTest -w \
35 *         com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
36 * </code>
37 */
38@SmallTest
39public class NameSplitterTest extends TestCase {
40    private NameSplitter mNameSplitter;
41
42    @Override
43    protected void setUp() throws Exception {
44        super.setUp();
45        createNameSplitter(Locale.US);
46    }
47
48    private void createNameSplitter(Locale locale) {
49        mNameSplitter = new NameSplitter("Mr, Ms, Mrs", "d', st, st., von", "Jr., M.D., MD, D.D.S.",
50                "&, AND", locale);
51    }
52
53    public void testNull() {
54        assertSplitName(null, null, null, null, null, null);
55        assertJoinedName(null, null, null, null, null, null);
56    }
57
58    public void testEmpty() {
59        assertSplitName("", null, null, null, null, null);
60        assertJoinedName(null, null, null, null, null, null);
61    }
62
63    public void testSpaces() {
64        assertSplitName(" ", null, null, null, null, null);
65        assertJoinedName(null, null, null, null, null, null);
66    }
67
68    public void testFamilyName() {
69        assertSplitName("Smith", null, "Smith", null, null, null);
70        assertJoinedName("Smith", null, "Smith", null, null, null);
71    }
72
73    public void testIgnoreSuffix() {
74        assertSplitName("Ms MD", "Ms", null, null, "MD", null);
75        assertJoinedName("Ms MD", "Ms", null, null, "MD", null);
76    }
77
78    public void testGivenFamilyName() {
79        assertSplitName("John Smith", null, "John", null, "Smith", null);
80        assertJoinedName("John Smith", null, "John", null, "Smith", null);
81    }
82
83    public void testGivenMiddleFamilyName() {
84        assertSplitName("John Edward Smith", null, "John", "Edward", "Smith", null);
85        assertJoinedName("John Edward Smith", null, "John", "Edward", "Smith", null);
86    }
87
88    public void testThreeNamesAndFamilyName() {
89        assertSplitName("John Edward Kevin Smith", null, "John Edward", "Kevin", "Smith", null);
90        assertJoinedName("John Edward Kevin Smith", null, "John Edward", "Kevin", "Smith", null);
91    }
92
93    public void testPrefixFivenFamilyName() {
94        assertSplitName("Mr. John Smith", "Mr.", "John", null, "Smith", null);
95        assertJoinedName("Mr John Smith", "Mr", "John", null, "Smith", null);
96        assertSplitName("Mr.John Smith", "Mr.", "John", null, "Smith", null);
97        assertJoinedName("Mr John Smith", "Mr", "John", null, "Smith", null);
98    }
99
100    public void testFivenFamilyNameSuffix() {
101        assertSplitName("John Smith Jr", null, "John", null, "Smith", "Jr");
102        assertJoinedName("John Smith, Jr.", null, "John", null, "Smith", "Jr");
103    }
104
105    public void testGivenFamilyNameSuffixWithDot() {
106        assertSplitName("John Smith M.D.", null, "John", null, "Smith", "M.D.");
107        assertJoinedName("John Smith, M.D.", null, "John", null, "Smith", "M.D.");
108        assertSplitName("John Smith D D S", null, "John", null, "Smith", "D D S");
109        assertJoinedName("John Smith, D D S", null, "John", null, "Smith", "D D S");
110    }
111
112    public void testGivenSuffixFamilyName() {
113        assertSplitName("John von Smith", null, "John", null, "von Smith", null);
114        assertJoinedName("John von Smith", null, "John", null, "von Smith", null);
115    }
116
117    public void testGivenSuffixFamilyNameWithDot() {
118        assertSplitName("John St.Smith", null, "John", null, "St. Smith", null);
119        assertJoinedName("John St. Smith", null, "John", null, "St. Smith", null);
120    }
121
122    public void testPrefixGivenMiddleFamily() {
123        assertSplitName("Mr. John Kevin Smith", "Mr.", "John", "Kevin", "Smith", null);
124        assertJoinedName("Mr John Kevin Smith", "Mr", "John", "Kevin", "Smith", null);
125        assertSplitName("Mr.John Kevin Smith", "Mr.", "John", "Kevin", "Smith", null);
126        assertJoinedName("Mr. John Kevin Smith", "Mr.", "John", "Kevin", "Smith", null);
127    }
128
129    public void testPrefixGivenMiddleFamilySuffix() {
130        assertSplitName("Mr. John Kevin Smith Jr.", "Mr.", "John", "Kevin", "Smith", "Jr.");
131        assertJoinedName("Mr John Kevin Smith, Jr.", "Mr", "John", "Kevin", "Smith", "Jr");
132    }
133
134    public void testPrefixGivenMiddlePrefixFamilySuffixWrongCapitalization() {
135        assertSplitName("MR. john keVin VON SmiTh JR.", "MR.", "john", "keVin", "VON SmiTh", "JR.");
136        assertJoinedName("MR john keVin VON SmiTh, JR.", "MR", "john", "keVin", "VON SmiTh", "JR");
137    }
138
139    public void testPrefixFamilySuffix() {
140        assertSplitName("von Smith Jr.", null, null, null, "von Smith", "Jr.");
141        assertJoinedName("von Smith, Jr.", null, null, null, "von Smith", "Jr");
142    }
143
144    public void testFamilyNameGiven() {
145        assertSplitName("Smith, John", null, "John", null, "Smith", null);
146        assertSplitName("Smith  , John", null, "John", null, "Smith", null);
147        assertSplitName("Smith, John Kimble", null, "John", "Kimble", "Smith", null);
148        assertSplitName("Smith, John K.", null, "John", "K.", "Smith", null);
149        assertSplitName("Smith, John, Jr.", null, "John", null, "Smith", "Jr.");
150        assertSplitName("Smith, John Kimble, Jr.", null, "John", "Kimble", "Smith", "Jr.");
151        assertSplitName("von Braun, John, Jr.", null, "John", null, "von Braun", "Jr.");
152        assertSplitName("von Braun, John Kimble, Jr.", null, "John", "Kimble", "von Braun", "Jr.");
153    }
154
155    public void testTwoNamesAndFamilyNameWithAmpersand() {
156        assertSplitName("John & Edward Smith", null, "John & Edward", null, "Smith", null);
157        assertJoinedName("John & Edward Smith", null, "John & Edward", null, "Smith", null);
158        assertSplitName("John and Edward Smith", null, "John and Edward", null, "Smith", null);
159        assertSplitName("Smith, John and Edward", null, "John and Edward", null, "Smith", null);
160        assertJoinedName("John and Edward Smith", null, "John and Edward", null, "Smith", null);
161    }
162
163    public void testWithMiddleInitialAndNoDot() {
164        assertSplitName("John E. Smith", null, "John", "E.", "Smith", null);
165        assertJoinedName("John E Smith", null, "John", "E", "Smith", null);
166    }
167
168    public void testWithLongGivenNameAndDot() {
169        assertSplitName("John Ed. K. Smith", null, "John Ed.", "K.", "Smith", null);
170        assertJoinedName("John Ed. K Smith", null, "John Ed.", "K", "Smith", null);
171    }
172
173    public void testGuessFullNameStyleEmpty() {
174        assertFullNameStyle(FullNameStyle.UNDEFINED, null);
175        assertFullNameStyle(FullNameStyle.UNDEFINED, "");
176    }
177
178    public void testGuessFullNameStyleWestern() {
179
180        // Latin letters
181        assertFullNameStyle(FullNameStyle.WESTERN, "John Doe");
182
183        // Starts with a Latin letter, but contains Japanese letters
184        assertFullNameStyle(FullNameStyle.JAPANESE, "A\u3080\u308D\u306A\u307F\u3048");
185
186        // Starts with an Extended Latin letter "Latin Capital Ligature OE"
187        assertFullNameStyle(FullNameStyle.WESTERN, "\u0152uvre");
188
189        // Non-letters don't make a difference. This one starts with a vertical line
190        assertFullNameStyle(FullNameStyle.WESTERN, "\uFF5C.?+Smith");
191    }
192
193    public void testGuessFullNameStyleJapanese() {
194        createNameSplitter(Locale.JAPAN);
195
196        // Hiragana: always Japanese
197        assertFullNameStyle(FullNameStyle.JAPANESE, "\u3042\u3080\u308D\u306A\u307F\u3048");
198
199        // Katakana: always Japanese
200        assertFullNameStyle(FullNameStyle.JAPANESE, "\u30A2\u30E0\u30ED \u30CA\u30DF\u30A8");
201
202        // Half-width Katakana: always Japanese
203        assertFullNameStyle(FullNameStyle.JAPANESE, "\uFF71\uFF91\uFF9B \uFF85\uFF90\uFF74");
204
205        // Kanji: we cannot tell if this is Japanese, Chinese or Korean, but we are
206        // in Locale.JAPAN, so assume Japanese
207        assertFullNameStyle(FullNameStyle.JAPANESE, "\u5B89\u5BA4\u5948\u7F8E\u6075");
208
209        // TODO: mix
210
211        // Accompanied by a phonetic name in Hiragana, we can safely assume that the
212        // name is Japanese
213        assertFullNameStyle(FullNameStyle.JAPANESE, "\u5B89\u5BA4\u5948\u7F8E\u6075",
214                "\u3042\u3080\u308D", null, "\u306A\u307F\u3048");
215
216        // Starts with a latin letter - not Western
217        assertFullNameStyle(FullNameStyle.JAPANESE, "A\u3080\u308D\u306A\u307F\u3048");
218    }
219
220    public void testGuessFullNameStyleChinese() {
221        createNameSplitter(Locale.CHINA);
222
223        // Hanzi: we cannot tell if this is Chinese, Japanese or Korean,
224        // but we are in Locale.CHINA, so assume this is Chinese
225        assertFullNameStyle(FullNameStyle.CHINESE, "\u675C\u9D51");
226
227        // Accompanied by a phonetic name in Pinyin, we can safely assume that the
228        // name is Chinese
229        assertFullNameStyle(FullNameStyle.CHINESE, "\u675C\u9D51",
230                "du4", null, "juan1");
231
232        // Non-letters don't make a difference. This one starts with a vertical line
233        assertFullNameStyle(FullNameStyle.CHINESE, "\uFF5C--(\u675C\u9D51)");
234    }
235
236
237    public void testGuessPhoneticNameStyle() {
238
239        // Hiragana
240        assertPhoneticNameStyle(PhoneticNameStyle.JAPANESE, "\u3042\u3080\u308D", null, null);
241        assertPhoneticNameStyle(PhoneticNameStyle.JAPANESE, null, "\u3042\u3080\u308D", null);
242        assertPhoneticNameStyle(PhoneticNameStyle.JAPANESE, null, null, "\u306A\u307F\u3048");
243        assertPhoneticNameStyle(PhoneticNameStyle.JAPANESE, "\u3042\u3080\u308D", null,
244                "\u306A\u307F\u3048");
245
246        // Katakana
247        assertPhoneticNameStyle(PhoneticNameStyle.JAPANESE, "\u30A2\u30E0\u30ED", null,
248                "\u30CA\u30DF\u30A8");
249
250        // Half-width Katakana
251        assertPhoneticNameStyle(PhoneticNameStyle.JAPANESE, "\u30A2\u30E0\u30ED", null,
252                "\u30CA\u30DF\u30A8");
253
254        // Chinese
255        assertPhoneticNameStyle(PhoneticNameStyle.PINYIN, "du4", null, "juan1");
256    }
257
258    public void testSplitJapaneseName() {
259        createNameSplitter(Locale.JAPAN);
260
261        // One word is interpreted as given name only
262        assertSplitName("\u3042\u3080\u308D", null, "\u3042\u3080\u308D", null, null, null);
263
264        // Two words are interpreted as family + give name
265        assertSplitName("\u3042\u3080\u308D \u306A\u307F\u3048", null, "\u306A\u307F\u3048", null,
266                "\u3042\u3080\u308D", null);
267
268        // Multiple words are interpreted as "family - given names"
269        assertSplitName("\u3042\u3080\u308D \u3068\u304A\u308B \u306A\u307F\u3048", null,
270                "\u3068\u304A\u308B \u306A\u307F\u3048", null, "\u3042\u3080\u308D", null);
271
272        // Hanzi characters without spaces: lump them all in the given name
273        assertSplitName("\u6BB5\u5C0F\u6D9B", null, "\u6BB5\u5C0F\u6D9B", null, null, null);
274    }
275
276    public void testSplitChineseName() {
277        createNameSplitter(Locale.CHINA);
278
279        // Two Hanzi characters: familyName+givenName
280        assertSplitName("\u6BB5\u5C0F", null, "\u5C0F", null, "\u6BB5", null);
281
282        // Two Hanzi characters: familyName+middleName+givenName
283        assertSplitName("\u6BB5\u5C0F\u6D9B", null, "\u6D9B", "\u5C0F", "\u6BB5", null);
284
285        // Two Hanzi characters: familyName(2)+middleName+givenName
286        assertSplitName("\u6BB5\u5C0F\u6D9B\u6D9C", null, "\u6D9C", "\u6D9B", "\u6BB5\u5C0F", null);
287    }
288
289    public void testJoinJapaneseName() {
290        createNameSplitter(Locale.JAPAN);
291
292        assertJoinedName("\u3042\u3080\u308D", FullNameStyle.JAPANESE, null, "\u3042\u3080\u308D",
293                null, null, null, true);
294
295        // Given-name-first flag is ignored for CJK locales
296        assertJoinedName("\u3084\u307E\u3056\u304D \u3068\u304A\u308B", FullNameStyle.JAPANESE,
297                null, "\u3068\u304A\u308B", null, "\u3084\u307E\u3056\u304D", null, false);
298        assertJoinedName("\u3084\u307E\u3056\u304D \u3068\u304A\u308B \u3068\u304A\u308B",
299                FullNameStyle.JAPANESE, null, "\u3068\u304A\u308B", "\u3068\u304A\u308B",
300                "\u3084\u307E\u3056\u304D", null, false);
301    }
302
303    public void testJoinChineseName() {
304        createNameSplitter(Locale.CHINA);
305
306        // Given-name-first flag is ignored for CJK locales
307        assertJoinedName("\u6BB5\u5C0F\u6D9B", FullNameStyle.CHINESE, null,
308                "\u6D9B", "\u5C0F", "\u6BB5", null, true);
309        assertJoinedName("\u6BB5\u5C0F\u6D9B", FullNameStyle.CHINESE, null,
310                "\u6D9B", "\u5C0F", "\u6BB5", null, false);
311    }
312
313    private void assertSplitName(String fullName, String prefix, String givenNames,
314            String middleName, String familyName, String suffix) {
315        final Name name = new Name();
316        mNameSplitter.split(name, fullName);
317        assertEquals(prefix, name.getPrefix());
318        assertEquals(givenNames, name.getGivenNames());
319        assertEquals(middleName, name.getMiddleName());
320        assertEquals(familyName, name.getFamilyName());
321        assertEquals(suffix, name.getSuffix());
322    }
323
324    private void assertJoinedName(String expected, String prefix, String givenNames,
325            String middleName, String familyName, String suffix) {
326        assertJoinedName(expected, FullNameStyle.WESTERN, prefix, givenNames, middleName,
327                familyName, suffix, true);
328    }
329
330    private void assertJoinedName(String expected, int nameStyle, String prefix, String givenNames,
331            String middleName, String familyName, String suffix, boolean givenNameFirst) {
332        Name name = new Name();
333        name.fullNameStyle = nameStyle;
334        name.prefix = prefix;
335        name.givenNames = givenNames;
336        name.middleName = middleName;
337        name.familyName = familyName;
338        name.suffix = suffix;
339        String actual = mNameSplitter.join(name, givenNameFirst, true);
340        assertEquals(expected, actual);
341    }
342
343    private void assertFullNameStyle(int expectedFullNameStyle, String fullName) {
344        Name name = new Name();
345        mNameSplitter.split(name, fullName);
346        mNameSplitter.guessNameStyle(name);
347
348        assertEquals(expectedFullNameStyle, name.fullNameStyle);
349    }
350
351    private void assertFullNameStyle(int expectedFullNameStyle, String fullName,
352            String phoneticFamilyName, String phoneticMiddleName, String phoneticGivenName) {
353        Name name = new Name();
354        mNameSplitter.split(name, fullName);
355        name.phoneticFamilyName = phoneticFamilyName;
356        name.phoneticMiddleName = phoneticMiddleName;
357        name.phoneticGivenName = phoneticGivenName;
358
359        mNameSplitter.guessNameStyle(name);
360
361        assertEquals(expectedFullNameStyle, name.fullNameStyle);
362    }
363
364    private void assertPhoneticNameStyle(int expectedPhoneticNameStyle, String phoneticFamilyName,
365            String phoneticMiddleName, String phoneticGivenName) {
366        Name name = new Name();
367        name.phoneticFamilyName = phoneticFamilyName;
368        name.phoneticMiddleName = phoneticMiddleName;
369        name.phoneticGivenName = phoneticGivenName;
370
371        mNameSplitter.guessNameStyle(name);
372
373        assertEquals(expectedPhoneticNameStyle, name.phoneticNameStyle);
374    }
375
376    public void testSplitKoreanName() {
377        createNameSplitter(Locale.KOREA);
378
379        // Lee - Sang Il
380        assertSplitName("\uC774\uC0C1\uC77C", null, "\uC0C1\uC77C", null, "\uC774", null);
381        // Dok Go - Young Jae
382        assertSplitName("\uB3C5\uACE0\uC601\uC7AC",
383                null, "\uC601\uC7AC", null, "\uB3C5\uACE0", null);
384    }
385}
386