Support multiple locales for font language settings.

Some fonts support multiple scripts, for example, some fonts for
Korean supports not only "Kore" but also "Jamo".

To select fonts based on their multiple languages, this CL introduces
the following changes:
- Compares all languages of the font family and use the maximum score
  for font selection.
- Even if each language of the font family doesn't support the requested
  language, the font get score of 2 if the requested font is covered by
  all of the languages of the font family. For example, the font for
  "ko-Hang,ko-Hani" gets score of 2 for the requested language "ko-Kore".

Bug: 26687969

Change-Id: I7f13b51464c9b01982bb573251d77052b9ddbd70
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 5f669a1..75cbd76 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -177,9 +177,15 @@
     }
 
     if (vs == 0xFE0F || vs == 0xFE0E) {
-        // TODO use all language in the list.
-        const FontLanguage lang = FontLanguageListCache::getById(fontFamily->langId())[0];
-        const bool hasEmojiFlag = lang.hasEmojiFlag();
+        const FontLanguages& langs = FontLanguageListCache::getById(fontFamily->langId());
+        bool hasEmojiFlag = false;
+        for (size_t i = 0; i < langs.size(); ++i) {
+            if (langs[i].hasEmojiFlag()) {
+                hasEmojiFlag = true;
+                break;
+            }
+        }
+
         if (vs == 0xFE0F) {
             return hasEmojiFlag ? 2 : 1;
         } else {  // vs == 0xFE0E
@@ -208,13 +214,12 @@
 uint32_t FontCollection::calcLanguageMatchingScore(
         uint32_t userLangListId, const FontFamily& fontFamily) {
     const FontLanguages& langList = FontLanguageListCache::getById(userLangListId);
-    // TODO use all language in the list.
-    FontLanguage fontLanguage = FontLanguageListCache::getById(fontFamily.langId())[0];
+    const FontLanguages& fontLanguages = FontLanguageListCache::getById(fontFamily.langId());
 
     const size_t maxCompareNum = std::min(langList.size(), FONT_LANGUAGES_LIMIT);
-    uint32_t score = fontLanguage.getScoreFor(langList[0]);  // maxCompareNum can't be zero.
-    for (size_t i = 1; i < maxCompareNum; ++i) {
-        score = score * 3u + fontLanguage.getScoreFor(langList[i]);
+    uint32_t score = 0;
+    for (size_t i = 0; i < maxCompareNum; ++i) {
+        score = score * 3u + langList[i].calcScoreFor(fontLanguages);
     }
     return score;
 }
diff --git a/libs/minikin/FontLanguage.cpp b/libs/minikin/FontLanguage.cpp
index db63059..bccb4bf 100644
--- a/libs/minikin/FontLanguage.cpp
+++ b/libs/minikin/FontLanguage.cpp
@@ -126,24 +126,59 @@
     return other.mScript == mScript;
 }
 
-bool FontLanguage::supportsScript(uint8_t requestedBits) const {
-    return requestedBits != 0 && (mSubScriptBits & requestedBits) == requestedBits;
+// static
+bool FontLanguage::supportsScript(uint8_t providedBits, uint8_t requestedBits) {
+    return requestedBits != 0 && (providedBits & requestedBits) == requestedBits;
 }
 
 bool FontLanguage::supportsHbScript(hb_script_t script) const {
     static_assert(SCRIPT_TAG('J', 'p', 'a', 'n') == HB_TAG('J', 'p', 'a', 'n'),
                   "The Minikin script and HarfBuzz hb_script_t have different encodings.");
     if (script == mScript) return true;
-    return supportsScript(scriptToSubScriptBits(script));
+    return supportsScript(mSubScriptBits, scriptToSubScriptBits(script));
 }
 
-int FontLanguage::getScoreFor(const FontLanguage other) const {
-    if (isUnsupported() || other.isUnsupported()) {
-        return 0;
-    } else if (isEqualScript(other) || supportsScript(other.mSubScriptBits)) {
-        return mLanguage == other.mLanguage ? 2 : 1;
-    } else {
-        return 0;
+int FontLanguage::calcScoreFor(const FontLanguages& supported) const {
+    int score = 0;
+    for (size_t i = 0; i < supported.size(); ++i) {
+        if (isEqualScript(supported[i]) ||
+                supportsScript(supported[i].mSubScriptBits, mSubScriptBits)) {
+            if (mLanguage == supported[i].mLanguage) {
+                return 2;
+            } else {
+                score = 1;
+            }
+        }
+    }
+
+    if (score == 1) {
+        return score;
+    }
+
+    if (supportsScript(supported.getUnionOfSubScriptBits(), mSubScriptBits)) {
+        // Gives score of 2 only if the language matches all of the font languages except for the
+        // exact match case handled above.
+        return (mLanguage == supported[0].mLanguage && supported.isAllTheSameLanguage()) ? 2 : 1;
+    }
+
+    return 0;
+}
+
+FontLanguages::FontLanguages(std::vector<FontLanguage>&& languages)
+    : mLanguages(std::move(languages)) {
+    if (mLanguages.empty()) {
+        return;
+    }
+
+    const FontLanguage& lang = mLanguages[0];
+
+    mIsAllTheSameLanguage = true;
+    mUnionOfSubScriptBits = lang.mSubScriptBits;
+    for (size_t i = 1; i < mLanguages.size(); ++i) {
+        mUnionOfSubScriptBits |= mLanguages[i].mSubScriptBits;
+        if (mIsAllTheSameLanguage && lang.mLanguage != mLanguages[i].mLanguage) {
+            mIsAllTheSameLanguage = false;
+        }
     }
 }
 
diff --git a/libs/minikin/FontLanguage.h b/libs/minikin/FontLanguage.h
index 1a20480..f944174 100644
--- a/libs/minikin/FontLanguage.h
+++ b/libs/minikin/FontLanguage.h
@@ -24,6 +24,11 @@
 
 namespace android {
 
+// Due to the limits in font fallback score calculation, we can't use anything more than 17
+// languages.
+const size_t FONT_LANGUAGES_LIMIT = 17;
+class FontLanguages;
+
 // FontLanguage is a compact representation of a BCP 47 language tag. It
 // does not capture all possible information, only what directly affects
 // font rendering.
@@ -54,12 +59,16 @@
 
     std::string getString() const;
 
+    // Calculates a matching score. This score represents how well the input languages cover this
+    // language. The maximum score in the language list is returned.
     // 0 = no match, 1 = script match, 2 = script and primary language match.
-    int getScoreFor(const FontLanguage other) const;
+    int calcScoreFor(const FontLanguages& supported) const;
 
     uint64_t getIdentifier() const { return (uint64_t)mScript << 32 | (uint64_t)mLanguage; }
 
 private:
+    friend class FontLanguages;  // for FontLanguages constructor
+
     // ISO 15924 compliant script code. The 4 chars script code are packed into a 32 bit integer.
     uint32_t mScript;
 
@@ -80,12 +89,37 @@
     uint8_t mSubScriptBits;
 
     static uint8_t scriptToSubScriptBits(uint32_t script);
-    bool supportsScript(uint8_t requestedBits) const;
+
+    // Returns true if the provide subscript bits has the requested subscript bits.
+    // Note that this function returns false if the requested subscript bits are empty.
+    static bool supportsScript(uint8_t providedBits, uint8_t requestedBits);
 };
 
-// Due to the limit of font fallback cost calculation, we can't use anything more than 17 languages.
-const size_t FONT_LANGUAGES_LIMIT = 17;
-typedef std::vector<FontLanguage> FontLanguages;
+// An immutable list of languages.
+class FontLanguages {
+public:
+    FontLanguages(std::vector<FontLanguage>&& languages);
+    FontLanguages() : mUnionOfSubScriptBits(0), mIsAllTheSameLanguage(false) {}
+    FontLanguages(FontLanguages&&) = default;
+
+    size_t size() const { return mLanguages.size(); }
+    bool empty() const { return mLanguages.empty(); }
+    const FontLanguage& operator[] (size_t n) const { return mLanguages[n]; }
+
+private:
+    friend struct FontLanguage;  // for calcScoreFor
+
+    std::vector<FontLanguage> mLanguages;
+    uint8_t mUnionOfSubScriptBits;
+    bool mIsAllTheSameLanguage;
+
+    uint8_t getUnionOfSubScriptBits() const { return mUnionOfSubScriptBits; }
+    bool isAllTheSameLanguage() const { return mIsAllTheSameLanguage; }
+
+    // Do not copy and assign.
+    FontLanguages(const FontLanguages&) = delete;
+    void operator=(const FontLanguages&) = delete;
+};
 
 }  // namespace android
 
diff --git a/libs/minikin/FontLanguageListCache.cpp b/libs/minikin/FontLanguageListCache.cpp
index 5d177b5..6b661f0 100644
--- a/libs/minikin/FontLanguageListCache.cpp
+++ b/libs/minikin/FontLanguageListCache.cpp
@@ -76,8 +76,8 @@
     return outLength;
 }
 
-static FontLanguages constructFontLanguages(const std::string& input) {
-    FontLanguages result;
+static std::vector<FontLanguage> parseLanguageList(const std::string& input) {
+    std::vector<FontLanguage> result;
     size_t currentIdx = 0;
     size_t commaLoc = 0;
     char langTag[ULOC_FULLNAME_CAPACITY];
@@ -121,11 +121,11 @@
 
     // Given language list is not in cache. Insert it and return newly assigned ID.
     const uint32_t nextId = inst->mLanguageLists.size();
-    FontLanguages fontLanguages = constructFontLanguages(languages);
+    FontLanguages fontLanguages(parseLanguageList(languages));
     if (fontLanguages.empty()) {
         return kEmptyListId;
     }
-    inst->mLanguageLists.push_back(fontLanguages);
+    inst->mLanguageLists.push_back(std::move(fontLanguages));
     inst->mLanguageListLookupTable.insert(std::make_pair(languages, nextId));
     return nextId;
 }
@@ -146,7 +146,7 @@
 
         // Insert an empty language list for mapping default language list to kEmptyListId.
         // The default language list has only one FontLanguage and it is the unsupported language.
-        instance->mLanguageLists.push_back(FontLanguages({FontLanguage()}));
+        instance->mLanguageLists.push_back(FontLanguages());
         instance->mLanguageListLookupTable.insert(std::make_pair("", kEmptyListId));
     }
     return instance;
diff --git a/tests/FontCollectionItemizeTest.cpp b/tests/FontCollectionItemizeTest.cpp
index 22971c8..45587e9 100644
--- a/tests/FontCollectionItemizeTest.cpp
+++ b/tests/FontCollectionItemizeTest.cpp
@@ -698,72 +698,104 @@
 TEST_F(FontCollectionItemizeTest, itemize_LanguageScore) {
     struct TestCase {
         std::string userPreferredLanguages;
-        std::string fontLanguages;
+        std::vector<std::string> fontLanguages;
         int selectedFontIndex;
     } testCases[] = {
+        // Font can specify empty language.
+        { "und", { "", "" }, 0 },
+        { "und", { "", "en-Latn" }, 0 },
+        { "en-Latn", { "", "" }, 0 },
+        { "en-Latn", { "", "en-Latn" }, 1 },
+
         // Single user preferred language.
         // Exact match case
-        { "en-Latn", "en-Latn,ja-Jpan", 0 },
-        { "ja-Jpan", "en-Latn,ja-Jpan", 1 },
-        { "en-Latn", "en-Latn,nl-Latn,es-Latn", 0 },
-        { "nl-Latn", "en-Latn,nl-Latn,es-Latn", 1 },
-        { "es-Latn", "en-Latn,nl-Latn,es-Latn", 2 },
-        { "es-Latn", "en-Latn,en-Latn,nl-Latn", 0 },
+        { "en-Latn", { "en-Latn", "ja-Jpan" }, 0 },
+        { "ja-Jpan", { "en-Latn", "ja-Jpan" }, 1 },
+        { "en-Latn", { "en-Latn", "nl-Latn", "es-Latn" }, 0 },
+        { "nl-Latn", { "en-Latn", "nl-Latn", "es-Latn" }, 1 },
+        { "es-Latn", { "en-Latn", "nl-Latn", "es-Latn" }, 2 },
+        { "es-Latn", { "en-Latn", "en-Latn", "nl-Latn" }, 0 },
 
         // Exact script match case
-        { "en-Latn", "nl-Latn,be-Latn", 0 },
-        { "en-Arab", "nl-Latn,ar-Arab", 1 },
-        { "en-Latn", "be-Latn,ar-Arab,bd-Beng", 0 },
-        { "en-Arab", "be-Latn,ar-Arab,bd-Beng", 1 },
-        { "en-Beng", "be-Latn,ar-Arab,bd-Beng", 2 },
-        { "en-Beng", "be-Latn,ar-Beng,bd-Beng", 1 },
-        { "zh-Hant", "zh-Hant,zh-Hans", 0 },
-        { "zh-Hans", "zh-Hant,zh-Hans", 1 },
+        { "en-Latn", { "nl-Latn", "e-Latn" }, 0 },
+        { "en-Arab", { "nl-Latn", "ar-Arab" }, 1 },
+        { "en-Latn", { "be-Latn", "ar-Arab", "d-Beng" }, 0 },
+        { "en-Arab", { "be-Latn", "ar-Arab", "d-Beng" }, 1 },
+        { "en-Beng", { "be-Latn", "ar-Arab", "d-Beng" }, 2 },
+        { "en-Beng", { "be-Latn", "ar-Beng", "d-Beng" }, 1 },
+        { "zh-Hant", { "zh-Hant", "zh-Hans" }, 0 },
+        { "zh-Hans", { "zh-Hant", "zh-Hans" }, 1 },
 
         // Subscript match case, e.g. Jpan supports Hira.
-        { "en-Hira", "ja-Jpan", 0 },
-        { "zh-Hani", "zh-Hans,zh-Hant", 0 },
-        { "zh-Hani", "zh-Hant,zh-Hans", 0 },
-        { "en-Hira", "zh-Hant,ja-Jpan,ja-Jpan", 1 },
+        { "en-Hira", { "ja-Jpan" }, 0 },
+        { "zh-Hani", { "zh-Hans", "zh-Hant" }, 0 },
+        { "zh-Hani", { "zh-Hant", "zh-Hans" }, 0 },
+        { "en-Hira", { "zh-Hant", "ja-Jpan", "ja-Jpan" }, 1 },
 
         // Language match case
-        { "ja-Latn", "zh-Latn,ja-Latn", 1 },
-        { "zh-Latn", "zh-Latn,ja-Latn", 0 },
-        { "ja-Latn", "zh-Latn,ja-Latn", 1 },
-        { "ja-Latn", "zh-Latn,ja-Latn,ja-Latn", 1 },
+        { "ja-Latn", { "zh-Latn", "ja-Latn" }, 1 },
+        { "zh-Latn", { "zh-Latn", "ja-Latn" }, 0 },
+        { "ja-Latn", { "zh-Latn", "ja-Latn" }, 1 },
+        { "ja-Latn", { "zh-Latn", "ja-Latn", "ja-Latn" }, 1 },
 
         // Mixed case
         // Script/subscript match is strongest.
-        { "ja-Jpan", "en-Latn,ja-Latn,en-Jpan", 2 },
-        { "ja-Hira", "en-Latn,ja-Latn,en-Jpan", 2 },
-        { "ja-Hira", "en-Latn,ja-Latn,en-Jpan,en-Jpan", 2 },
+        { "ja-Jpan", { "en-Latn", "ja-Latn", "en-Jpan" }, 2 },
+        { "ja-Hira", { "en-Latn", "ja-Latn", "en-Jpan" }, 2 },
+        { "ja-Hira", { "en-Latn", "ja-Latn", "en-Jpan", "en-Jpan" }, 2 },
 
         // Language match only happens if the script matches.
-        { "ja-Hira", "en-Latn,ja-Latn", 0 },
-        { "ja-Hira", "en-Jpan,ja-Jpan", 1 },
+        { "ja-Hira", { "en-Latn", "ja-Latn" }, 0 },
+        { "ja-Hira", { "en-Jpan", "ja-Jpan" }, 1 },
 
         // Multiple languages.
         // Even if all fonts have the same score, use the 2nd language for better selection.
-        { "en-Latn,ja-Jpan", "zh-Hant,zh-Hans,ja-Jpan", 2 },
-        { "en-Latn,nl-Latn", "es-Latn,be-Latn,nl-Latn", 2 },
-        { "en-Latn,br-Latn,nl-Latn", "es-Latn,be-Latn,nl-Latn", 2 },
-        { "en-Latn,br-Latn,nl-Latn", "es-Latn,be-Latn,nl-Latn,nl-Latn", 2 },
+        { "en-Latn,ja-Jpan", { "zh-Hant", "zh-Hans", "ja-Jpan" }, 2 },
+        { "en-Latn,nl-Latn", { "es-Latn", "be-Latn", "nl-Latn" }, 2 },
+        { "en-Latn,br-Latn,nl-Latn", { "es-Latn", "be-Latn", "nl-Latn" }, 2 },
+        { "en-Latn,br-Latn,nl-Latn", { "es-Latn", "be-Latn", "nl-Latn", "nl-Latn" }, 2 },
 
         // Script score.
-        { "en-Latn,ja-Jpan", "en-Arab,en-Jpan", 1 },
-        { "en-Latn,ja-Jpan", "en-Arab,en-Jpan,en-Jpan", 1 },
+        { "en-Latn,ja-Jpan", { "en-Arab", "en-Jpan" }, 1 },
+        { "en-Latn,ja-Jpan", { "en-Arab", "en-Jpan", "en-Jpan" }, 1 },
 
         // Language match case
-        { "en-Latn,ja-Latn", "bd-Latn,ja-Latn", 1 },
-        { "en-Latn,ja-Latn", "bd-Latn,ja-Latn,ja-Latn", 1 },
+        { "en-Latn,ja-Latn", { "bd-Latn", "ja-Latn" }, 1 },
+        { "en-Latn,ja-Latn", { "bd-Latn", "ja-Latn", "ja-Latn" }, 1 },
 
         // Language match only happens if the script matches.
-        { "en-Latn,ar-Arab", "en-Beng,ar-Arab", 1 },
+        { "en-Latn,ar-Arab", { "en-Beng", "ar-Arab" }, 1 },
+
+        // Multiple languages in the font settings.
+        { "ko-Jamo", { "ja-Jpan", "ko-Kore", "ko-Kore,ko-Jamo"}, 2 },
+        { "en-Latn", { "ja-Jpan", "en-Latn,ja-Jpan"}, 1 },
+        { "en-Latn", { "ja-Jpan", "ja-Jpan,en-Latn"}, 1 },
+        { "en-Latn", { "ja-Jpan,zh-Hant", "en-Latn,ja-Jpan", "en-Latn"}, 1 },
+        { "en-Latn", { "zh-Hant,ja-Jpan", "ja-Jpan,en-Latn", "en-Latn"}, 1 },
+
+        // Kore = Hang + Hani, etc.
+        { "ko-Kore", { "ko-Hang", "ko-Jamo,ko-Hani", "ko-Hang,ko-Hani"}, 2 },
+        { "ja-Hrkt", { "ja-Hira", "ja-Kana", "ja-Hira,ja-Kana"}, 2 },
+        { "ja-Jpan", { "ja-Hira", "ja-Kana", "ja-Hani", "ja-Hira,ja-Kana,ja-Hani"}, 3 },
+        { "zh-Hanb", { "zh-Hant", "zh-Bopo", "zh-Hant,zh-Bopo"}, 2 },
+        { "zh-Hanb", { "ja-Hanb", "zh-Hant,zh-Bopo"}, 1 },
+
+        // Language match with unified subscript bits.
+        { "zh-Hanb", { "zh-Hant", "zh-Bopo", "ja-Hant,ja-Bopo", "zh-Hant,zh-Bopo"}, 3 },
+        { "zh-Hanb", { "zh-Hant", "zh-Bopo", "ja-Hant,zh-Bopo", "zh-Hant,zh-Bopo"}, 3 },
     };
 
     for (auto testCase : testCases) {
+        std::string fontLanguagesStr = "{";
+        for (size_t i = 0; i < testCase.fontLanguages.size(); ++i) {
+            if (i != 0) {
+                fontLanguagesStr += ", ";
+            }
+            fontLanguagesStr += "\"" + testCase.fontLanguages[i] + "\"";
+        }
+        fontLanguagesStr += "}";
         SCOPED_TRACE("Test of user preferred languages: \"" + testCase.userPreferredLanguages +
-                     "\" with font languages: " + testCase.fontLanguages);
+                     "\" with font languages: " + fontLanguagesStr);
 
         std::vector<FontFamily*> families;
 
@@ -778,12 +810,10 @@
         // Each font family is associated with a specified language. All font families except for
         // the first font support U+9AA8.
         std::unordered_map<MinikinFont*, int> fontLangIdxMap;
-        const FontLanguages& fontLanguages = registerAndGetFontLanguages(testCase.fontLanguages);
 
-        for (size_t i = 0; i < fontLanguages.size(); ++i) {
-            const FontLanguage& fontLanguage = fontLanguages[i];
+        for (size_t i = 0; i < testCase.fontLanguages.size(); ++i) {
             FontFamily* family = new FontFamily(
-                    FontStyle::registerLanguageList(fontLanguage.getString()), 0 /* variant */);
+                    FontStyle::registerLanguageList(testCase.fontLanguages[i]), 0 /* variant */);
             MinikinFont* minikin_font = new MinikinFontForTest(kJAFont);
             family->addFont(minikin_font);
             families.push_back(family);
diff --git a/tests/FontFamilyTest.cpp b/tests/FontFamilyTest.cpp
index 34a9296..907f395 100644
--- a/tests/FontFamilyTest.cpp
+++ b/tests/FontFamilyTest.cpp
@@ -30,7 +30,7 @@
 typedef ICUTestBase FontLanguagesTest;
 typedef ICUTestBase FontLanguageTest;
 
-static FontLanguages createFontLanguages(const std::string& input) {
+static const FontLanguages& createFontLanguages(const std::string& input) {
     AutoMutex _l(gMinikinLock);
     uint32_t langId = FontLanguageListCache::getId(input);
     return FontLanguageListCache::getById(langId);
@@ -217,35 +217,30 @@
     EXPECT_EQ(0u, emptyLangs.size());
 
     FontLanguage english = createFontLanguage("en");
-    FontLanguages singletonLangs = createFontLanguages("en");
+    const FontLanguages& singletonLangs = createFontLanguages("en");
     EXPECT_EQ(1u, singletonLangs.size());
     EXPECT_EQ(english, singletonLangs[0]);
 
     FontLanguage french = createFontLanguage("fr");
-    FontLanguages twoLangs = createFontLanguages("en,fr");
+    const FontLanguages& twoLangs = createFontLanguages("en,fr");
     EXPECT_EQ(2u, twoLangs.size());
     EXPECT_EQ(english, twoLangs[0]);
     EXPECT_EQ(french, twoLangs[1]);
 }
 
 TEST_F(FontLanguagesTest, unsupportedLanguageTests) {
-    FontLanguage unsupportedLang = createFontLanguage("abcd");
-    ASSERT_TRUE(unsupportedLang.isUnsupported());
+    const FontLanguages& oneUnsupported = createFontLanguages("abcd-example");
+    EXPECT_TRUE(oneUnsupported.empty());
 
-    FontLanguages oneUnsupported = createFontLanguages("abcd-example");
-    EXPECT_EQ(1u, oneUnsupported.size());
-    EXPECT_TRUE(oneUnsupported[0].isUnsupported());
-
-    FontLanguages twoUnsupporteds = createFontLanguages("abcd-example,abcd-example");
-    EXPECT_EQ(1u, twoUnsupporteds.size());
-    EXPECT_TRUE(twoUnsupporteds[0].isUnsupported());
+    const FontLanguages& twoUnsupporteds = createFontLanguages("abcd-example,abcd-example");
+    EXPECT_TRUE(twoUnsupporteds.empty());
 
     FontLanguage english = createFontLanguage("en");
-    FontLanguages firstUnsupported = createFontLanguages("abcd-example,en");
+    const FontLanguages& firstUnsupported = createFontLanguages("abcd-example,en");
     EXPECT_EQ(1u, firstUnsupported.size());
     EXPECT_EQ(english, firstUnsupported[0]);
 
-    FontLanguages lastUnsupported = createFontLanguages("en,abcd-example");
+    const FontLanguages& lastUnsupported = createFontLanguages("en,abcd-example");
     EXPECT_EQ(1u, lastUnsupported.size());
     EXPECT_EQ(english, lastUnsupported[0]);
 }
@@ -256,20 +251,20 @@
     FontLanguage englishInLatn = createFontLanguage("en-Latn");
     ASSERT_TRUE(english == englishInLatn);
 
-    FontLanguages langs = createFontLanguages("en,en-Latn");
+    const FontLanguages& langs = createFontLanguages("en,en-Latn");
     EXPECT_EQ(1u, langs.size());
     EXPECT_EQ(english, langs[0]);
 
     // Country codes are ignored.
-    FontLanguages fr = createFontLanguages("fr,fr-CA,fr-FR");
+    const FontLanguages& fr = createFontLanguages("fr,fr-CA,fr-FR");
     EXPECT_EQ(1u, fr.size());
     EXPECT_EQ(french, fr[0]);
 
     // The order should be kept.
-    langs = createFontLanguages("en,fr,en-Latn");
-    EXPECT_EQ(2u, langs.size());
-    EXPECT_EQ(english, langs[0]);
-    EXPECT_EQ(french, langs[1]);
+    const FontLanguages& langs2 = createFontLanguages("en,fr,en-Latn");
+    EXPECT_EQ(2u, langs2.size());
+    EXPECT_EQ(english, langs2[0]);
+    EXPECT_EQ(french, langs2[1]);
 }
 
 TEST_F(FontLanguagesTest, undEmojiTests) {
diff --git a/tests/FontLanguageListCacheTest.cpp b/tests/FontLanguageListCacheTest.cpp
index fbbd937..2a04671 100644
--- a/tests/FontLanguageListCacheTest.cpp
+++ b/tests/FontLanguageListCacheTest.cpp
@@ -56,18 +56,18 @@
     FontLanguage english = FontLanguageListCache::getById(enLangId)[0];
     FontLanguage japanese = FontLanguageListCache::getById(jpLangId)[0];
 
-    FontLanguages defLangs = FontLanguageListCache::getById(0);
-    EXPECT_EQ(1UL, defLangs.size());
-    EXPECT_TRUE(defLangs[0].isUnsupported());
+    const FontLanguages& defLangs = FontLanguageListCache::getById(0);
+    EXPECT_TRUE(defLangs.empty());
 
-    FontLanguages langs = FontLanguageListCache::getById(FontLanguageListCache::getId("en"));
+    const FontLanguages& langs = FontLanguageListCache::getById(FontLanguageListCache::getId("en"));
     ASSERT_EQ(1UL, langs.size());
     EXPECT_EQ(english, langs[0]);
 
-    langs = FontLanguageListCache::getById(FontLanguageListCache::getId("en,jp"));
-    ASSERT_EQ(2UL, langs.size());
-    EXPECT_EQ(english, langs[0]);
-    EXPECT_EQ(japanese, langs[1]);
+    const FontLanguages& langs2 =
+            FontLanguageListCache::getById(FontLanguageListCache::getId("en,jp"));
+    ASSERT_EQ(2UL, langs2.size());
+    EXPECT_EQ(english, langs2[0]);
+    EXPECT_EQ(japanese, langs2[1]);
 }
 
 }  // android