blob: ca81eddd66b1a350e17dc63f0efbfa6be6f9f683 [file] [log] [blame]
package io.flutter.plugin.localization;
import static io.flutter.Build.API_LEVELS;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.LocaleList;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import java.util.Locale;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(AndroidJUnit4.class)
@TargetApi(API_LEVELS.API_24) // LocaleList and scriptCode are API 24+.
public class LocalizationPluginTest {
private final Context ctx = ApplicationProvider.getApplicationContext();
// This test should be synced with the version for API 24.
@Test
@Config(sdk = API_LEVELS.API_26)
public void computePlatformResolvedLocaleAPI26() {
// --- Test Setup ---
FlutterJNI flutterJNI = new FlutterJNI();
Context context = mock(Context.class);
Resources resources = mock(Resources.class);
Configuration config = mock(Configuration.class);
DartExecutor dartExecutor = mock(DartExecutor.class);
LocaleList localeList =
new LocaleList(new Locale("es", "MX"), new Locale("zh", "CN"), new Locale("en", "US"));
when(context.getResources()).thenReturn(resources);
when(resources.getConfiguration()).thenReturn(config);
when(config.getLocales()).thenReturn(localeList);
flutterJNI.setLocalizationPlugin(
new LocalizationPlugin(context, new LocalizationChannel(dartExecutor)));
// Empty supportedLocales.
String[] supportedLocales = new String[] {};
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 0);
// Empty preferredLocales.
supportedLocales =
new String[] {
"fr", "FR", "",
"zh", "", "",
"en", "CA", ""
};
localeList = new LocaleList();
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
// The first locale is default.
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "FR");
assertEquals(result[2], "");
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"fr", "FR", "",
"it", "IT", ""
};
localeList = new LocaleList(new Locale("fr", "CH"));
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
// The call will use the new (> API 24) algorithm.
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "FR");
assertEquals(result[2], "");
supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"fr", "FR", "",
"fr", "", "",
"it", "IT", ""
};
localeList = new LocaleList(new Locale("fr", "CH"));
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
// The call will use the new (> API 24) algorithm.
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "");
assertEquals(result[2], "");
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"it", "IT", ""
};
localeList = new LocaleList(new Locale("fr", "CH"), new Locale("it", "CH"));
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
// The call will use the new (> API 24) algorithm.
assertEquals(result.length, 3);
assertEquals(result[0], "it");
assertEquals(result[1], "IT");
assertEquals(result[2], "");
supportedLocales =
new String[] {
"zh", "CN", "Hans",
"zh", "HK", "Hant",
};
localeList = new LocaleList(new Locale("zh", "CN"));
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 3);
assertEquals(result[0], "zh");
assertEquals(result[1], "CN");
assertEquals(result[2], "Hans");
}
// This test should be synced with the version for API 26.
@Test
@Config(minSdk = API_LEVELS.API_24)
public void computePlatformResolvedLocale_fromAndroidN() {
// --- Test Setup ---
FlutterJNI flutterJNI = new FlutterJNI();
Context context = mock(Context.class);
Resources resources = mock(Resources.class);
Configuration config = mock(Configuration.class);
DartExecutor dartExecutor = mock(DartExecutor.class);
LocaleList localeList =
new LocaleList(new Locale("es", "MX"), new Locale("zh", "CN"), new Locale("en", "US"));
when(context.getResources()).thenReturn(resources);
when(resources.getConfiguration()).thenReturn(config);
when(config.getLocales()).thenReturn(localeList);
flutterJNI.setLocalizationPlugin(
new LocalizationPlugin(context, new LocalizationChannel(dartExecutor)));
// Empty supportedLocales.
String[] supportedLocales = new String[] {};
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 0);
// Empty preferredLocales.
supportedLocales =
new String[] {
"fr", "FR", "",
"zh", "", "",
"en", "CA", ""
};
localeList = new LocaleList();
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
// The first locale is default.
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "FR");
assertEquals(result[2], "");
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"fr", "FR", "",
"it", "IT", ""
};
localeList = new LocaleList(new Locale("fr", "CH"));
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
// The call will use the new (> API 24) algorithm.
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "FR");
assertEquals(result[2], "");
supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"fr", "FR", "",
"fr", "", "",
"it", "IT", ""
};
localeList = new LocaleList(new Locale("fr", "CH"));
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
// The call will use the new (> API 24) algorithm.
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "");
assertEquals(result[2], "");
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"it", "IT", ""
};
localeList = new LocaleList(new Locale("fr", "CH"), new Locale("it", "CH"));
when(config.getLocales()).thenReturn(localeList);
result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
// The call will use the new (> API 24) algorithm.
assertEquals(result.length, 3);
assertEquals(result[0], "it");
assertEquals(result[1], "IT");
assertEquals(result[2], "");
}
// Tests the legacy pre API 24 algorithm.
@Test
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "es-rMX")
public void computePlatformResolvedLocale_emptySupportedLocales_beforeAndroidN() {
FlutterJNI flutterJNI = new FlutterJNI();
DartExecutor dartExecutor = mock(DartExecutor.class);
flutterJNI.setLocalizationPlugin(
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
String[] supportedLocales = new String[] {};
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 0);
}
@Test
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "")
public void computePlatformResolvedLocale_selectFirstLocaleWhenNoUserSetting_beforeAndroidN() {
FlutterJNI flutterJNI = new FlutterJNI();
DartExecutor dartExecutor = mock(DartExecutor.class);
flutterJNI.setLocalizationPlugin(
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
String[] supportedLocales =
new String[] {
"fr", "FR", "",
"zh", "", "",
"en", "CA", ""
};
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "FR");
assertEquals(result[2], "");
}
@Test
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "fr-rCH")
public void computePlatformResolvedLocale_selectFirstLocaleWhenNoExactMatch_beforeAndroidN() {
FlutterJNI flutterJNI = new FlutterJNI();
DartExecutor dartExecutor = mock(DartExecutor.class);
flutterJNI.setLocalizationPlugin(
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
String[] supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"fr", "FR", "",
"it", "IT", ""
};
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 3);
assertEquals(result[0], "en");
assertEquals(result[1], "");
assertEquals(result[2], "");
}
@Test
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "it-rIT")
public void computePlatformResolvedLocale_selectExactMatchLocale_beforeAndroidN() {
FlutterJNI flutterJNI = new FlutterJNI();
DartExecutor dartExecutor = mock(DartExecutor.class);
flutterJNI.setLocalizationPlugin(
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
String[] supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"fr", "FR", "",
"it", "IT", ""
};
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 3);
assertEquals(result[0], "it");
assertEquals(result[1], "IT");
assertEquals(result[2], "");
}
@Test
@Config(minSdk = API_LEVELS.API_21, maxSdk = API_LEVELS.API_23, qualifiers = "fr-rCH")
public void computePlatformResolvedLocale_selectOnlyLanguageLocale_beforeAndroidN() {
FlutterJNI flutterJNI = new FlutterJNI();
DartExecutor dartExecutor = mock(DartExecutor.class);
flutterJNI.setLocalizationPlugin(
new LocalizationPlugin(ctx, new LocalizationChannel(dartExecutor)));
String[] supportedLocales =
new String[] {
"en", "", "",
"de", "DE", "",
"es", "ES", "",
"fr", "FR", "",
"fr", "", "",
"it", "IT", ""
};
String[] result = flutterJNI.computePlatformResolvedLocale(supportedLocales);
assertEquals(result.length, 3);
assertEquals(result[0], "fr");
assertEquals(result[1], "");
assertEquals(result[2], "");
}
@Test
public void localeFromString_languageOnly() {
Locale locale = LocalizationPlugin.localeFromString("en");
assertEquals(locale, new Locale("en"));
}
@Test
public void localeFromString_languageAndCountry() {
Locale locale = LocalizationPlugin.localeFromString("en-US");
assertEquals(locale, new Locale("en", "US"));
}
@Test
public void localeFromString_languageCountryAndVariant() {
Locale locale = LocalizationPlugin.localeFromString("zh-Hans-CN");
assertEquals(locale, new Locale("zh", "CN", "Hans"));
}
@Test
public void localeFromString_underscore() {
Locale locale = LocalizationPlugin.localeFromString("zh_Hans_CN");
assertEquals(locale, new Locale("zh", "CN", "Hans"));
}
@Test
public void localeFromString_additionalVariantsAreIgnored() {
Locale locale = LocalizationPlugin.localeFromString("de-DE-u-co-phonebk");
assertEquals(locale, new Locale("de", "DE"));
}
@Test
public void getStringResource_withoutLocale() throws JSONException {
Context context = mock(Context.class);
Resources resources = mock(Resources.class);
DartExecutor dartExecutor = mock(DartExecutor.class);
LocalizationChannel localizationChannel = new LocalizationChannel(dartExecutor);
LocalizationPlugin plugin = new LocalizationPlugin(context, localizationChannel);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
String fakePackageName = "package_name";
String fakeKey = "test_key";
int fakeId = 123;
when(context.getPackageName()).thenReturn(fakePackageName);
when(context.getResources()).thenReturn(resources);
when(resources.getIdentifier(fakeKey, "string", fakePackageName)).thenReturn(fakeId);
when(resources.getString(fakeId)).thenReturn("test_value");
JSONObject param = new JSONObject();
param.put("key", fakeKey);
localizationChannel.handler.onMethodCall(
new MethodCall("Localization.getStringResource", param), mockResult);
verify(mockResult).success("test_value");
}
@Test
public void getStringResource_withLocale() throws JSONException {
Context context = mock(Context.class);
Context localContext = mock(Context.class);
Resources resources = mock(Resources.class);
Resources localResources = mock(Resources.class);
Configuration configuration = new Configuration();
DartExecutor dartExecutor = mock(DartExecutor.class);
LocalizationChannel localizationChannel = new LocalizationChannel(dartExecutor);
LocalizationPlugin plugin = new LocalizationPlugin(context, localizationChannel);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
String fakePackageName = "package_name";
String fakeKey = "test_key";
int fakeId = 123;
when(context.getPackageName()).thenReturn(fakePackageName);
when(context.createConfigurationContext(any())).thenReturn(localContext);
when(context.getResources()).thenReturn(resources);
when(localContext.getResources()).thenReturn(localResources);
when(resources.getConfiguration()).thenReturn(configuration);
when(localResources.getIdentifier(fakeKey, "string", fakePackageName)).thenReturn(fakeId);
when(localResources.getString(fakeId)).thenReturn("test_value");
JSONObject param = new JSONObject();
param.put("key", fakeKey);
param.put("locale", "en-US");
localizationChannel.handler.onMethodCall(
new MethodCall("Localization.getStringResource", param), mockResult);
verify(mockResult).success("test_value");
}
@Test
public void getStringResource_nonExistentKey() throws JSONException {
Context context = mock(Context.class);
Resources resources = mock(Resources.class);
DartExecutor dartExecutor = mock(DartExecutor.class);
LocalizationChannel localizationChannel = new LocalizationChannel(dartExecutor);
LocalizationPlugin plugin = new LocalizationPlugin(context, localizationChannel);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
String fakePackageName = "package_name";
String fakeKey = "test_key";
when(context.getPackageName()).thenReturn(fakePackageName);
when(context.getResources()).thenReturn(resources);
when(resources.getIdentifier(fakeKey, "string", fakePackageName))
.thenReturn(0); // 0 means not exist
JSONObject param = new JSONObject();
param.put("key", fakeKey);
localizationChannel.handler.onMethodCall(
new MethodCall("Localization.getStringResource", param), mockResult);
verify(mockResult).success(null);
}
}