tests for locale selection
This commit is contained in:
		
							parent
							
								
									87d4779c2d
								
							
						
					
					
						commit
						3406edefcd
					
				@ -19,6 +19,7 @@ import android.text.TextUtils;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
import androidx.annotation.RequiresApi;
 | 
			
		||||
import com.fasterxml.jackson.annotation.JacksonInject;
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonIgnore;
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty;
 | 
			
		||||
@ -544,7 +545,7 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= 24) {
 | 
			
		||||
            LocaleList localeList = Resources.getSystem().getConfiguration().getLocales();
 | 
			
		||||
            LocaleList localeList = getLocales();
 | 
			
		||||
            String[] sortedLocaleList = localeList.toLanguageTags().split(",");
 | 
			
		||||
            Arrays.sort(sortedLocaleList, new java.util.Comparator<String>() {
 | 
			
		||||
                @Override
 | 
			
		||||
@ -624,6 +625,11 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
 | 
			
		||||
        tvScreenshots = getLocalizedListEntry(localized, localesToUse, "tvScreenshots");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @RequiresApi(api = Build.VERSION_CODES.N)
 | 
			
		||||
    LocaleList getLocales() {
 | 
			
		||||
        return Resources.getSystem().getConfiguration().getLocales();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the right localized version of this entry, based on an immitation of
 | 
			
		||||
     * the logic that Android/Java uses.  On Android >= 24, this can get the
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,8 @@ import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.lang.reflect.Field;
 | 
			
		||||
import java.lang.reflect.Modifier;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertEquals;
 | 
			
		||||
@ -177,4 +179,15 @@ public class TestUtils {
 | 
			
		||||
        AppProvider.Helper.calcSuggestedApks(context);
 | 
			
		||||
        AppProvider.Helper.recalculatePreferredMetadata(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set a static final field through reflection
 | 
			
		||||
     */
 | 
			
		||||
    public static void setFinalStatic(Field field, Object newValue) throws Exception {
 | 
			
		||||
        field.setAccessible(true);
 | 
			
		||||
        Field modifiersField = Field.class.getDeclaredField("modifiers");
 | 
			
		||||
        modifiersField.setAccessible(true);
 | 
			
		||||
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
 | 
			
		||||
        field.set(null, newValue);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,194 @@
 | 
			
		||||
package org.fdroid.fdroid.data;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.LocaleList;
 | 
			
		||||
import org.fdroid.fdroid.TestUtils;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.runner.RunWith;
 | 
			
		||||
import org.robolectric.RobolectricTestRunner;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertEquals;
 | 
			
		||||
import static org.junit.Assert.assertTrue;
 | 
			
		||||
import static org.mockito.Mockito.doReturn;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.spy;
 | 
			
		||||
import static org.mockito.Mockito.when;
 | 
			
		||||
 | 
			
		||||
@RunWith(RobolectricTestRunner.class)
 | 
			
		||||
public class LocaleSelectionTest {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void correctLocaleSelectionBeforeSDK24() throws Exception {
 | 
			
		||||
        TestUtils.setFinalStatic(Build.VERSION.class.getDeclaredField("SDK_INT"), 19);
 | 
			
		||||
        assertTrue(Build.VERSION.SDK_INT < 24);
 | 
			
		||||
        App app;
 | 
			
		||||
 | 
			
		||||
        Map<String, Map<String, Object>> localized = new HashMap<>();
 | 
			
		||||
        HashMap<String, Object> en_US = new HashMap<>();
 | 
			
		||||
        en_US.put("summary", "summary-en_US");
 | 
			
		||||
        HashMap<String, Object> de_AT = new HashMap<>();
 | 
			
		||||
        de_AT.put("summary", "summary-de_AT");
 | 
			
		||||
        HashMap<String, Object> de_DE = new HashMap<>();
 | 
			
		||||
        de_DE.put("summary", "summary-de_DE");
 | 
			
		||||
        HashMap<String, Object> sv = new HashMap<>();
 | 
			
		||||
        sv.put("summary", "summary-sv");
 | 
			
		||||
        HashMap<String, Object> sv_FI = new HashMap<>();
 | 
			
		||||
        sv_FI.put("summary", "summary-sv_FI");
 | 
			
		||||
 | 
			
		||||
        localized.put("de-AT", de_AT);
 | 
			
		||||
        localized.put("de-DE", de_DE);
 | 
			
		||||
        localized.put("en-US", en_US);
 | 
			
		||||
        localized.put("sv", sv);
 | 
			
		||||
        localized.put("sv-FI", sv_FI);
 | 
			
		||||
 | 
			
		||||
        // Easy mode. en-US metadata with an en-US locale
 | 
			
		||||
        Locale.setDefault(new Locale("en", "US"));
 | 
			
		||||
        app = new App();
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-en_US", app.summary);
 | 
			
		||||
 | 
			
		||||
        // Fall back to en-US locale, when we have a different en locale
 | 
			
		||||
        Locale.setDefault(new Locale("en", "UK"));
 | 
			
		||||
        app = new App();
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-en_US", app.summary);
 | 
			
		||||
 | 
			
		||||
        // Fall back to language only
 | 
			
		||||
        Locale.setDefault(new Locale("en", "UK"));
 | 
			
		||||
        app = new App();
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-en_US", app.summary);
 | 
			
		||||
 | 
			
		||||
        // select the correct one out of multiple language locales
 | 
			
		||||
        Locale.setDefault(new Locale("de", "DE"));
 | 
			
		||||
        app = new App();
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-de_DE", app.summary);
 | 
			
		||||
 | 
			
		||||
        // Even when we have a non-exact matching locale, we should fall back to the same language
 | 
			
		||||
        Locale.setDefault(new Locale("de", "CH"));
 | 
			
		||||
        app = new App();
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-de_AT", app.summary);
 | 
			
		||||
 | 
			
		||||
        // Test fallback to base lang with not exact matching locale
 | 
			
		||||
        Locale.setDefault(new Locale("sv", "SE"));
 | 
			
		||||
        app = new App();
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-sv", app.summary);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void correctLocaleSelectionFromSDK24() throws Exception {
 | 
			
		||||
 | 
			
		||||
        TestUtils.setFinalStatic(Build.VERSION.class.getDeclaredField("SDK_INT"), 29);
 | 
			
		||||
        assertTrue(Build.VERSION.SDK_INT >= 24);
 | 
			
		||||
 | 
			
		||||
        App app = spy(new App());
 | 
			
		||||
        LocaleList localeList = mock(LocaleList.class);
 | 
			
		||||
 | 
			
		||||
        // we mock both the getLocales call and the conversion to a language tag string.
 | 
			
		||||
        doReturn(localeList).when(app).getLocales();
 | 
			
		||||
        // Set both default locale as well as the locale list, because the algorithm uses both...
 | 
			
		||||
        Locale.setDefault(new Locale("en", "US"));
 | 
			
		||||
        when(localeList.toLanguageTags()).thenReturn("en-US,de-DE");
 | 
			
		||||
 | 
			
		||||
        //no metadata present
 | 
			
		||||
        Map<String, Map<String, Object>> localized = new HashMap<>();
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("Unknown application", app.summary);
 | 
			
		||||
 | 
			
		||||
        HashMap<String, Object> en_US = new HashMap<>();
 | 
			
		||||
        en_US.put("summary", "summary-en_US");
 | 
			
		||||
        HashMap<String, Object> en_GB = new HashMap<>();
 | 
			
		||||
        en_GB.put("summary", "summary-en_GB");
 | 
			
		||||
        HashMap<String, Object> de_AT = new HashMap<>();
 | 
			
		||||
        de_AT.put("summary", "summary-de_AT");
 | 
			
		||||
        HashMap<String, Object> de_DE = new HashMap<>();
 | 
			
		||||
        de_DE.put("summary", "summary-de_DE");
 | 
			
		||||
 | 
			
		||||
        app.summary = "reset";
 | 
			
		||||
        localized.put("de-AT", de_AT);
 | 
			
		||||
        localized.put("de-DE", de_DE);
 | 
			
		||||
        localized.put("en-US", en_US);
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        // just select the matching en-US locale, nothing special here
 | 
			
		||||
        assertEquals("summary-en_US", app.summary);
 | 
			
		||||
 | 
			
		||||
        Locale.setDefault(new Locale("en", "SE"));
 | 
			
		||||
        when(localeList.toLanguageTags()).thenReturn("en-SE,de-DE");
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        // Fall back to another en locale before de
 | 
			
		||||
        assertEquals("summary-en_US", app.summary);
 | 
			
		||||
 | 
			
		||||
        app.summary = "reset";
 | 
			
		||||
        localized.clear();
 | 
			
		||||
        localized.put("de-AT", de_AT);
 | 
			
		||||
        localized.put("de-DE", de_DE);
 | 
			
		||||
        localized.put("en-GB", en_GB);
 | 
			
		||||
        localized.put("en-US", en_US);
 | 
			
		||||
 | 
			
		||||
        Locale.setDefault(new Locale("de", "AT"));
 | 
			
		||||
        when(localeList.toLanguageTags()).thenReturn("de-AT,de-DE");
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        // full match against a non-default locale
 | 
			
		||||
        assertEquals("summary-de_AT", app.summary);
 | 
			
		||||
 | 
			
		||||
        app.summary = "reset";
 | 
			
		||||
        localized.clear();
 | 
			
		||||
        localized.put("de-AT", de_AT);
 | 
			
		||||
        localized.put("de", de_DE);
 | 
			
		||||
        localized.put("en-GB", en_GB);
 | 
			
		||||
        localized.put("en-US", en_US);
 | 
			
		||||
 | 
			
		||||
        Locale.setDefault(new Locale("de", "CH"));
 | 
			
		||||
        when(localeList.toLanguageTags()).thenReturn("de-CH,en-US");
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-de_DE", app.summary);
 | 
			
		||||
 | 
			
		||||
        app.summary = "reset";
 | 
			
		||||
        localized.clear();
 | 
			
		||||
        localized.put("en-GB", en_GB);
 | 
			
		||||
        localized.put("en-US", en_US);
 | 
			
		||||
 | 
			
		||||
        Locale.setDefault(new Locale("en", "AU"));
 | 
			
		||||
        when(localeList.toLanguageTags()).thenReturn("en-AU");
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-en_US", app.summary);
 | 
			
		||||
 | 
			
		||||
        app.summary = "reset";
 | 
			
		||||
        Locale.setDefault(new Locale("zh", "TW", "#Hant"));
 | 
			
		||||
        when(localeList.toLanguageTags()).thenReturn("zh-Hant-TW,zh-Hans-CN");
 | 
			
		||||
        localized.clear();
 | 
			
		||||
        localized.put("en", en_GB);
 | 
			
		||||
        localized.put("en-US", en_US);
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        //No match at all, fall back to an english locale
 | 
			
		||||
        assertEquals("summary-en_US", app.summary);
 | 
			
		||||
 | 
			
		||||
        app.summary = "reset";
 | 
			
		||||
        HashMap<String, Object> zh_TW = new HashMap<>();
 | 
			
		||||
        zh_TW.put("summary", "summary-zh_TW");
 | 
			
		||||
        HashMap<String, Object> zh_CN = new HashMap<>();
 | 
			
		||||
        zh_CN.put("summary", "summary-zh_CN");
 | 
			
		||||
 | 
			
		||||
        localized.clear();
 | 
			
		||||
        localized.put("en-US", en_US);
 | 
			
		||||
        localized.put("zh-CN", zh_CN);
 | 
			
		||||
        localized.put("zh-TW", zh_TW);
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-zh_TW", app.summary);
 | 
			
		||||
 | 
			
		||||
        localized.clear();
 | 
			
		||||
        localized.put("en-US", en_US);
 | 
			
		||||
        localized.put("zh-CN", zh_CN);
 | 
			
		||||
        app.setLocalized(localized);
 | 
			
		||||
        assertEquals("summary-zh_CN", app.summary);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user