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