1/*
2 * Copyright (C) 2016 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.internal.inputmethod;
18
19import android.os.LocaleList;
20import android.test.InstrumentationTestCase;
21import android.test.suitebuilder.annotation.SmallTest;
22
23import java.util.ArrayList;
24import java.util.Locale;
25
26public class LocaleUtilsTest extends InstrumentationTestCase {
27
28    private static final LocaleUtils.LocaleExtractor<Locale> sIdentityMapper =
29            new LocaleUtils.LocaleExtractor<Locale>() {
30                @Override
31                public Locale get(Locale source) {
32                    return source;
33                }
34            };
35
36    @SmallTest
37    public void testFilterByLanguageEmptyLanguageList() throws Exception {
38        final ArrayList<Locale> availableLocales = new ArrayList<>();
39        availableLocales.add(Locale.forLanguageTag("en-US"));
40        availableLocales.add(Locale.forLanguageTag("fr-CA"));
41        availableLocales.add(Locale.forLanguageTag("in"));
42        availableLocales.add(Locale.forLanguageTag("ja"));
43        availableLocales.add(Locale.forLanguageTag("fil"));
44
45        final LocaleList preferredLocales = LocaleList.getEmptyLocaleList();
46
47        final ArrayList<Locale> dest = new ArrayList<>();
48        LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
49        assertEquals(0, dest.size());
50    }
51
52    @SmallTest
53    public void testFilterDoesNotMatchAnything() throws Exception {
54        final ArrayList<Locale> availableLocales = new ArrayList<>();
55        availableLocales.add(Locale.forLanguageTag("en-US"));
56        availableLocales.add(Locale.forLanguageTag("fr-CA"));
57        availableLocales.add(Locale.forLanguageTag("in"));
58        availableLocales.add(Locale.forLanguageTag("ja"));
59        availableLocales.add(Locale.forLanguageTag("fil"));
60
61        final LocaleList preferredLocales = LocaleList.forLanguageTags("zh-Hans-TW");
62
63        final ArrayList<Locale> dest = new ArrayList<>();
64        LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
65        assertEquals(0, dest.size());
66    }
67
68    @SmallTest
69    public void testFilterByLanguageEmptySource() throws Exception {
70        final ArrayList<Locale> availableLocales = new ArrayList<>();
71
72        final LocaleList preferredLocales = LocaleList.forLanguageTags("fr,en-US,ja-JP");
73
74        final ArrayList<Locale> dest = new ArrayList<>();
75        LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
76        assertEquals(0, dest.size());
77    }
78
79    @SmallTest
80    public void testFilterByLanguageNullAvailableLocales() throws Exception {
81        {
82            final LocaleList preferredLocales =
83                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
84            final ArrayList<Locale> availableLocales = new ArrayList<>();
85            availableLocales.add(null);
86            final ArrayList<Locale> dest = new ArrayList<>();
87            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
88            assertEquals(0, dest.size());
89        }
90        {
91            final LocaleList preferredLocales =
92                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
93            final ArrayList<Locale> availableLocales = new ArrayList<>();
94            availableLocales.add(null);
95            availableLocales.add(null);
96            availableLocales.add(null);
97            final ArrayList<Locale> dest = new ArrayList<>();
98            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
99            assertEquals(0, dest.size());
100        }
101        {
102            final LocaleList preferredLocales =
103                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
104            final ArrayList<Locale> availableLocales = new ArrayList<>();
105            availableLocales.add(null);
106            availableLocales.add(Locale.forLanguageTag("en-US"));
107            availableLocales.add(null);
108            availableLocales.add(null);
109            final ArrayList<Locale> dest = new ArrayList<>();
110            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
111            assertEquals(1, dest.size());
112            assertEquals(availableLocales.get(1), dest.get(0));  // "en-US"
113        }
114        {
115            final LocaleList preferredLocales =
116                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
117            final ArrayList<Locale> availableLocales = new ArrayList<>();
118            availableLocales.add(null);
119            availableLocales.add(Locale.forLanguageTag("en"));
120            availableLocales.add(null);
121            availableLocales.add(null);
122            final ArrayList<Locale> dest = new ArrayList<>();
123            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
124            assertEquals(1, dest.size());
125            assertEquals(availableLocales.get(1), dest.get(0));  // "en"
126        }
127        {
128            final LocaleList preferredLocales =
129                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
130            final ArrayList<Locale> availableLocales = new ArrayList<>();
131            availableLocales.add(null);
132            availableLocales.add(Locale.forLanguageTag("ja-JP"));
133            availableLocales.add(null);
134            availableLocales.add(null);
135            final ArrayList<Locale> dest = new ArrayList<>();
136            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
137            assertEquals(0, dest.size());
138        }
139    }
140
141    @SmallTest
142    public void testFilterByLanguage() throws Exception {
143        {
144            final ArrayList<Locale> availableLocales = new ArrayList<>();
145            availableLocales.add(Locale.forLanguageTag("en-US"));
146            availableLocales.add(Locale.forLanguageTag("fr-CA"));
147            availableLocales.add(Locale.forLanguageTag("in"));
148            availableLocales.add(Locale.forLanguageTag("ja"));
149            availableLocales.add(Locale.forLanguageTag("fil"));
150
151            final LocaleList preferredLocales = LocaleList.forLanguageTags("fr,en-US,ja-JP");
152
153            final ArrayList<Locale> dest = new ArrayList<>();
154            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
155            assertEquals(3, dest.size());
156            assertEquals(availableLocales.get(1), dest.get(0));  // "fr-CA"
157            assertEquals(availableLocales.get(0), dest.get(1));  // "en-US"
158            assertEquals(availableLocales.get(3), dest.get(2));  // "ja"
159        }
160        {
161            final ArrayList<Locale> availableLocales = new ArrayList<>();
162            availableLocales.add(Locale.forLanguageTag("en-US"));
163            availableLocales.add(Locale.forLanguageTag("en-GB"));
164            availableLocales.add(Locale.forLanguageTag("en-IN"));
165
166            final LocaleList preferredLocales = LocaleList.forLanguageTags("en-US");
167
168            final ArrayList<Locale> dest = new ArrayList<>();
169            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
170            assertEquals(1, dest.size());
171            assertEquals(availableLocales.get(0), dest.get(0));  // "en-US"
172        }
173    }
174
175    @SmallTest
176    public void testFilterByLanguageTheSameLanguage() throws Exception {
177        {
178            final LocaleList preferredLocales =
179                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
180            final ArrayList<Locale> availableLocales = new ArrayList<>();
181            availableLocales.add(Locale.forLanguageTag("fr-CA"));
182            availableLocales.add(Locale.forLanguageTag("en-US"));
183            final ArrayList<Locale> dest = new ArrayList<>();
184            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
185            assertEquals(1, dest.size());
186            assertEquals(availableLocales.get(1), dest.get(0));  // "en-US"
187        }
188        {
189            final LocaleList preferredLocales =
190                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
191            final ArrayList<Locale> availableLocales = new ArrayList<>();
192            availableLocales.add(Locale.forLanguageTag("fr-CA"));
193            availableLocales.add(Locale.forLanguageTag("en"));
194            final ArrayList<Locale> dest = new ArrayList<>();
195            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
196            assertEquals(1, dest.size());
197            assertEquals(availableLocales.get(1), dest.get(0));  // "en"
198        }
199        {
200            final LocaleList preferredLocales =
201                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
202            final ArrayList<Locale> availableLocales = new ArrayList<>();
203            availableLocales.add(Locale.forLanguageTag("fr-CA"));
204            availableLocales.add(Locale.forLanguageTag("en-CA"));
205            availableLocales.add(Locale.forLanguageTag("en-IN"));
206            final ArrayList<Locale> dest = new ArrayList<>();
207            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
208            assertEquals(1, dest.size());
209            assertEquals(availableLocales.get(2), dest.get(0));  // "en-IN"
210        }
211        {
212            final LocaleList preferredLocales =
213                    LocaleList.forLanguageTags("en-AU,en-GB,en-US,en-IN");
214            final ArrayList<Locale> availableLocales = new ArrayList<>();
215            availableLocales.add(Locale.forLanguageTag("fr-CA"));
216            availableLocales.add(Locale.forLanguageTag("en-CA"));
217            availableLocales.add(Locale.forLanguageTag("en-NZ"));
218            availableLocales.add(Locale.forLanguageTag("en-BZ"));
219            final ArrayList<Locale> dest = new ArrayList<>();
220            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
221            assertEquals(1, dest.size());
222            assertEquals(availableLocales.get(1), dest.get(0));  // "en-CA"
223        }
224    }
225
226    @SmallTest
227    public void testFilterByLanguageFallbackRules() throws Exception {
228        {
229            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
230            final ArrayList<Locale> availableLocales = new ArrayList<>();
231            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA"));
232            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS"));
233            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME"));
234            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
235            availableLocales.add(Locale.forLanguageTag("sr-Latn-BA"));
236            availableLocales.add(Locale.forLanguageTag("sr-Latn-CS"));
237            availableLocales.add(Locale.forLanguageTag("sr-Latn-ME"));
238            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
239            final ArrayList<Locale> dest = new ArrayList<>();
240            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
241            assertEquals(1, dest.size());
242            assertEquals(availableLocales.get(7), dest.get(0));  // "sr-Latn-RS"
243        }
244        {
245            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS-x-android");
246            final ArrayList<Locale> availableLocales = new ArrayList<>();
247            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA"));
248            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS"));
249            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME"));
250            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
251            availableLocales.add(Locale.forLanguageTag("sr-Latn-BA"));
252            availableLocales.add(Locale.forLanguageTag("sr-Latn-CS"));
253            availableLocales.add(Locale.forLanguageTag("sr-Latn-ME"));
254            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
255            final ArrayList<Locale> dest = new ArrayList<>();
256            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
257            assertEquals(1, dest.size());
258            assertEquals(availableLocales.get(7), dest.get(0));  // "sr-Latn-RS"
259        }
260        {
261            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
262            final ArrayList<Locale> availableLocales = new ArrayList<>();
263            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA-x-android"));
264            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS-x-android"));
265            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME-x-android"));
266            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS-x-android"));
267            availableLocales.add(Locale.forLanguageTag("sr-Latn-BA-x-android"));
268            availableLocales.add(Locale.forLanguageTag("sr-Latn-CS-x-android"));
269            availableLocales.add(Locale.forLanguageTag("sr-Latn-ME-x-android"));
270            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS-x-android"));
271            final ArrayList<Locale> dest = new ArrayList<>();
272            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
273            assertEquals(1, dest.size());
274            assertEquals(availableLocales.get(7), dest.get(0));  // "sr-Latn-RS-x-android"
275        }
276
277        {
278            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
279            final ArrayList<Locale> availableLocales = new ArrayList<>();
280            availableLocales.add(Locale.forLanguageTag("sr"));
281            availableLocales.add(Locale.forLanguageTag("sr-Cyrl"));
282            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
283            final ArrayList<Locale> dest = new ArrayList<>();
284            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
285            assertEquals(1, dest.size());
286            assertEquals(availableLocales.get(2), dest.get(0));  // "sr-Latn"
287        }
288
289        {
290            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-RS");
291            final ArrayList<Locale> availableLocales = new ArrayList<>();
292            availableLocales.add(Locale.forLanguageTag("sr"));
293            availableLocales.add(Locale.forLanguageTag("sr-RS"));
294            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
295            final ArrayList<Locale> dest = new ArrayList<>();
296            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
297            assertEquals(1, dest.size());
298            assertEquals(availableLocales.get(0), dest.get(0));  // "sr"
299        }
300
301        {
302            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn");
303            final ArrayList<Locale> availableLocales = new ArrayList<>();
304            availableLocales.add(Locale.forLanguageTag("sr"));
305            availableLocales.add(Locale.forLanguageTag("sr-RS"));
306            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
307            final ArrayList<Locale> dest = new ArrayList<>();
308            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
309            assertEquals(1, dest.size());
310            assertEquals(availableLocales.get(2), dest.get(0));  // "sr-Latn"
311        }
312
313        {
314            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
315            final ArrayList<Locale> availableLocales = new ArrayList<>();
316            availableLocales.add(Locale.forLanguageTag("sr"));
317            availableLocales.add(Locale.forLanguageTag("sr-RS"));
318            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
319            final ArrayList<Locale> dest = new ArrayList<>();
320            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
321            assertEquals(1, dest.size());
322            assertEquals(availableLocales.get(0), dest.get(0));  // "sr"
323        }
324        {
325            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
326            final ArrayList<Locale> availableLocales = new ArrayList<>();
327            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
328            availableLocales.add(Locale.forLanguageTag("sr-RS"));
329            availableLocales.add(Locale.forLanguageTag("sr"));
330            final ArrayList<Locale> dest = new ArrayList<>();
331            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
332            assertEquals(1, dest.size());
333            assertEquals(availableLocales.get(1), dest.get(0));  // "sr-RS"
334        }
335
336        {
337            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
338            final ArrayList<Locale> availableLocales = new ArrayList<>();
339            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
340            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
341            final ArrayList<Locale> dest = new ArrayList<>();
342            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
343            assertEquals(1, dest.size());
344            assertEquals(availableLocales.get(0), dest.get(0));  // "sr-Cyrl-RS"
345        }
346        {
347            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn");
348            final ArrayList<Locale> availableLocales = new ArrayList<>();
349            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
350            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
351            final ArrayList<Locale> dest = new ArrayList<>();
352            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
353            assertEquals(1, dest.size());
354            assertEquals(availableLocales.get(0), dest.get(0));  // "sr-Latn-RS"
355        }
356    }
357
358    public void testFilterKnownLimitation() throws Exception {
359        // Following test cases are not for intentional behavior but checks for preventing the
360        // behavior from becoming worse.
361        {
362            final LocaleList preferredLocales = LocaleList.forLanguageTags("ja-Hrkt");
363            final ArrayList<Locale> availableLocales = new ArrayList<>();
364            availableLocales.add(Locale.forLanguageTag("ja-Jpan"));
365            availableLocales.add(Locale.forLanguageTag("ja-Hrkt"));
366            final ArrayList<Locale> dest = new ArrayList<>();
367            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
368            assertEquals(1, dest.size());
369            // Should be ja-Jpan since it supports ja-Hrkt and listed before ja-Hrkt.
370            assertEquals(availableLocales.get(1), dest.get(0));
371        }
372        {
373            final LocaleList preferredLocales = LocaleList.forLanguageTags("zh-Hani");
374            final ArrayList<Locale> availableLocales = new ArrayList<>();
375            availableLocales.add(Locale.forLanguageTag("zh-Hans"));
376            availableLocales.add(Locale.forLanguageTag("zh-Hant"));
377            availableLocales.add(Locale.forLanguageTag("zh-Hanb"));
378            availableLocales.add(Locale.forLanguageTag("zh-Hani"));
379            final ArrayList<Locale> dest = new ArrayList<>();
380            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
381            assertEquals(1, dest.size());
382            // Should be zh-Hans since it supports zh-Hani. Also zh-Hant, zh-Hanb supports zh-Hani.
383            assertEquals(availableLocales.get(3), dest.get(0));
384        }
385    }
386}
387