1 /*
2  * Copyright (C) 2022 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.app;
18 
19 import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.ArgumentMatchers.anyObject;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.Mockito.doReturn;
26 import static org.mockito.Mockito.spy;
27 
28 import android.os.LocaleList;
29 
30 import androidx.test.InstrumentationRegistry;
31 import androidx.test.filters.SmallTest;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import com.android.internal.app.LocaleStore.LocaleInfo;
35 
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 
40 import java.util.Collections;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.Locale;
44 import java.util.Set;
45 
46 /**
47  * Unit tests for the {@link AppLocaleCollector}.
48  */
49 @SmallTest
50 @RunWith(AndroidJUnit4.class)
51 public class AppLocaleCollectorTest {
52     private static final String TAG = "AppLocaleCollectorTest";
53     private AppLocaleCollector mAppLocaleCollector;
54     private LocaleStore.LocaleInfo mAppCurrentLocale;
55     private Set<LocaleInfo> mAllAppActiveLocales;
56     private Set<LocaleInfo> mImeLocales;
57     private Set<LocaleInfo> mSystemCurrentLocales;
58     private Set<LocaleInfo> mSystemSupportedLocales;
59     private AppLocaleStore.AppLocaleResult mResult;
60     private static final String PKG1 = "pkg1";
61     private static final int NONE = LocaleInfo.SUGGESTION_TYPE_NONE;
62     private static final int SIM = LocaleInfo.SUGGESTION_TYPE_SIM;
63     private static final int CFG = LocaleInfo.SUGGESTION_TYPE_CFG;
64     private static final int SIM_CFG = SIM | CFG;
65     private static final int CURRENT = LocaleInfo.SUGGESTION_TYPE_CURRENT;
66     private static final int SYSTEM = LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE;
67     private static final int OTHERAPP = LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE;
68     private static final int IME = LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE;
69     private static final int SYSTEM_AVAILABLE =
70             LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE;
71 
72     @Before
setUp()73     public void setUp() throws Exception {
74         mAppLocaleCollector = spy(
75                 new AppLocaleCollector(InstrumentationRegistry.getContext(), PKG1));
76     }
77 
78     @Test
testGetSystemCurrentLocales()79     public void testGetSystemCurrentLocales() {
80         LocaleList.setDefault(
81                 LocaleList.forLanguageTags("en-US-u-mu-fahrenhe,ar-JO-u-mu-fahrenhe-nu-latn"));
82 
83         Set<LocaleStore.LocaleInfo> list =
84                 mAppLocaleCollector.getSystemCurrentLocales();
85 
86         LocaleList expected = LocaleList.forLanguageTags("en-US,ar-JO-u-nu-latn");
87         assertEquals(list.size(), expected.size());
88         for (LocaleStore.LocaleInfo info : list) {
89             assertTrue(expected.indexOf(info.getLocale()) != -1);
90         }
91     }
92 
93     @Test
testGetSupportedLocaleList_filterNonAppsupportedSystemLanguage()94     public void testGetSupportedLocaleList_filterNonAppsupportedSystemLanguage() {
95         mAppCurrentLocale = createLocaleInfo("en-US", CURRENT);
96 
97         // App supports five locales
98         HashSet<Locale> appSupported =
99                 getAppSupportedLocales(new String[] {
100                         "en-US",
101                         "fr",
102                         "ar",
103                         "es",
104                         "bn"
105                 });
106         // There are six locales in system current locales.
107         mSystemCurrentLocales = getSystemCurrentLocales(new String[] {
108                 "en-US",
109                 "fr-FR",
110                 "ar-JO",
111                 "ca-AD",
112                 "da-DK",
113                 "es-US"
114         });
115         mAllAppActiveLocales = Collections.emptySet();
116         mImeLocales = Collections.emptySet();
117         mSystemSupportedLocales = Collections.emptySet();
118         mResult = new AppLocaleStore.AppLocaleResult(GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
119                 appSupported);
120 
121         doReturn(mAppCurrentLocale).when(mAppLocaleCollector).getAppCurrentLocale();
122         doReturn(mResult).when(mAppLocaleCollector).getAppSupportedLocales();
123         doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
124         doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
125         doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
126                 anyObject(), eq(null), eq(true));
127         doReturn(mSystemCurrentLocales).when(
128                 mAppLocaleCollector).getSystemCurrentLocales();
129 
130         Set<LocaleInfo> result = mAppLocaleCollector.getSupportedLocaleList(null, true, false);
131 
132         // The result would show four rather than six locales in the suggested region.
133         HashMap<String, Integer> expectedResult = new HashMap<>();
134         expectedResult.put("en-US", CURRENT); // The locale current App activates.
135         expectedResult.put("ar-JO", SYSTEM_AVAILABLE);
136         expectedResult.put("fr-FR", SYSTEM_AVAILABLE);
137         expectedResult.put("es-US", SYSTEM_AVAILABLE);
138         expectedResult.put(createLocaleInfo("", SYSTEM).getId(), SYSTEM); // System language title
139 
140         assertEquals(result.size(), expectedResult.size());
141         for (LocaleStore.LocaleInfo info: result) {
142             int suggestionFlags = expectedResult.getOrDefault(info.getId(), -1);
143             assertEquals(info.mSuggestionFlags, suggestionFlags);
144         }
145     }
146 
147     @Test
testGetSupportedLocaleList_withActiveLocalesFromOtherAppAndIme()148     public void testGetSupportedLocaleList_withActiveLocalesFromOtherAppAndIme() {
149         mAppCurrentLocale = createLocaleInfo("en-US", CURRENT);
150         mAllAppActiveLocales = initAllAppActivatedLocales();
151         mImeLocales = initImeLocales();
152         mSystemSupportedLocales = initSystemSupportedLocales();
153         mSystemCurrentLocales = initSystemCurrentLocales();
154         mResult = new AppLocaleStore.AppLocaleResult(GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
155                 initAppSupportedLocale());
156 
157         doReturn(mAppCurrentLocale).when(mAppLocaleCollector).getAppCurrentLocale();
158         doReturn(mResult).when(mAppLocaleCollector).getAppSupportedLocales();
159         doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
160         doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
161         doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
162                 anyObject(), eq(null), eq(true));
163         doReturn(mSystemCurrentLocales).when(
164                 mAppLocaleCollector).getSystemCurrentLocales();
165 
166         Set<LocaleInfo> result = mAppLocaleCollector.getSupportedLocaleList(null, true, false);
167 
168         HashMap<String, Integer> expectedResult = getExpectedResult();
169         assertEquals(result.size(), expectedResult.size());
170         for (LocaleInfo source : result) {
171             int suggestionFlags = expectedResult.getOrDefault(source.getId(), -1);
172             assertEquals(source.mSuggestionFlags, suggestionFlags);
173         }
174     }
175 
getExpectedResult()176     private HashMap<String, Integer> getExpectedResult() {
177         HashMap<String, Integer> map = new HashMap<>();
178         map.put("en-US", CURRENT); // The locale current App activates.
179         map.put("fr", NONE); // The locale App and system support.
180         map.put("zu", NONE); // The locale App and system support.
181         map.put("en", NONE); // Use en because System supports en while APP supports en-CA, en-GB.
182         map.put("ko", NONE); // The locale App and system support.
183         map.put("en-AU", OTHERAPP); // The locale other App activates and current App supports.
184         map.put("en-CA", OTHERAPP); // The locale other App activates and current App supports.
185         map.put("en-IN", IME); // The locale IME supports.
186         map.put("ja-JP",
187                 OTHERAPP | SYSTEM_AVAILABLE | IME); // The locale exists in OTHERAPP, SYSTEM and IME
188         map.put("zh-Hant-TW", SYSTEM_AVAILABLE);  // The locale system activates.
189         map.put(createLocaleInfo("", SYSTEM).getId(), SYSTEM); // System language title
190         return map;
191     }
192 
initSystemSupportedLocales()193     private Set<LocaleInfo> initSystemSupportedLocales() {
194         return Set.of(
195                 createLocaleInfo("en", NONE),
196                 createLocaleInfo("fr", NONE),
197                 createLocaleInfo("zu", NONE),
198                 createLocaleInfo("ko", NONE),
199                 // will be filtered because current App doesn't support.
200                 createLocaleInfo("es-US", SIM_CFG)
201         );
202     }
203 
initSystemCurrentLocales()204     private Set<LocaleInfo> initSystemCurrentLocales() {
205         return Set.of(createLocaleInfo("zh-Hant-TW", SYSTEM_AVAILABLE),
206                 createLocaleInfo("ja-JP", SYSTEM_AVAILABLE),
207                 // will be filtered because current App activates this locale.
208                 createLocaleInfo("en-US", SYSTEM_AVAILABLE));
209     }
210 
initAllAppActivatedLocales()211     private Set<LocaleInfo> initAllAppActivatedLocales() {
212         return Set.of(
213                 createLocaleInfo("en-CA", OTHERAPP),
214                 createLocaleInfo("en-AU", OTHERAPP),
215                 createLocaleInfo("ja-JP", OTHERAPP),
216                 // will be filtered because current App activates this locale.
217                 createLocaleInfo("en-US", OTHERAPP));
218     }
219 
initImeLocales()220     private Set<LocaleInfo> initImeLocales() {
221         return Set.of(
222                 // will be filtered because system activates zh-Hant-TW.
223                 createLocaleInfo("zh-TW", IME),
224                 // will be filtered because current App's activats this locale.
225                 createLocaleInfo("en-US", IME),
226                 createLocaleInfo("ja-JP", IME),
227                 createLocaleInfo("en-IN", IME));
228     }
229 
initAppSupportedLocale()230     private HashSet<Locale> initAppSupportedLocale() {
231         HashSet<Locale> hs = new HashSet();
232         hs.add(Locale.forLanguageTag("en-US"));
233         hs.add(Locale.forLanguageTag("en-CA"));
234         hs.add(Locale.forLanguageTag("en-GB"));
235         hs.add(Locale.forLanguageTag("zh-TW"));
236         hs.add(Locale.forLanguageTag("ja"));
237         hs.add(Locale.forLanguageTag("fr"));
238         hs.add(Locale.forLanguageTag("zu"));
239         hs.add(Locale.forLanguageTag("ko"));
240         // will be filtered because it's not in the system language.
241         hs.add(Locale.forLanguageTag("mn"));
242         return hs;
243     }
244 
getSystemCurrentLocales(String []languageTags)245     private Set<LocaleStore.LocaleInfo> getSystemCurrentLocales(String []languageTags) {
246         HashSet<LocaleStore.LocaleInfo> hs = new HashSet<>(languageTags.length);
247         for (String tag:languageTags) {
248             hs.add(createLocaleInfo(tag, SYSTEM_AVAILABLE));
249         }
250         return hs;
251     }
252 
getAppSupportedLocales(String []languageTags)253     private HashSet<Locale> getAppSupportedLocales(String []languageTags) {
254         HashSet<Locale> hs = new HashSet<>(languageTags.length);
255         for (String language:languageTags) {
256             hs.add(Locale.forLanguageTag(language));
257         }
258         return hs;
259     }
260 
createLocaleInfo(String languageTag, int suggestionFlag)261     private LocaleInfo createLocaleInfo(String languageTag, int suggestionFlag) {
262         LocaleInfo localeInfo = LocaleStore.fromLocale(Locale.forLanguageTag(languageTag));
263         localeInfo.mSuggestionFlags = suggestionFlag;
264         localeInfo.setTranslated(true);
265         return localeInfo;
266     }
267 }
268