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 
17 package com.android.internal.inputmethod;
18 
19 import android.os.LocaleList;
20 import android.test.InstrumentationTestCase;
21 import android.test.suitebuilder.annotation.SmallTest;
22 
23 import java.util.ArrayList;
24 import java.util.Locale;
25 
26 public 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
testFilterByLanguageEmptyLanguageList()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
testFilterDoesNotMatchAnything()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
testFilterByLanguageEmptySource()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
testFilterByLanguageNullAvailableLocales()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
testFilterByLanguage()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
testFilterByLanguageTheSameLanguage()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
testFilterByLanguageFallbackRules()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 
testFilterKnownLimitation()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