Home | History | Annotate | Download | only in unittest
      1 /*
      2  * Copyright (C) 2015 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 #include "minikin/FontFamily.h"
     18 
     19 #include <gtest/gtest.h>
     20 
     21 #include "minikin/LocaleList.h"
     22 
     23 #include "FontTestUtils.h"
     24 #include "FreeTypeMinikinFontForTest.h"
     25 #include "LocaleListCache.h"
     26 #include "MinikinInternal.h"
     27 
     28 namespace minikin {
     29 
     30 static const LocaleList& createLocaleList(const std::string& input) {
     31     uint32_t localeListId = LocaleListCache::getId(input);
     32     return LocaleListCache::getById(localeListId);
     33 }
     34 
     35 static Locale createLocale(const std::string& input) {
     36     uint32_t localeListId = LocaleListCache::getId(input);
     37     return LocaleListCache::getById(localeListId)[0];
     38 }
     39 
     40 static Locale createLocaleWithoutICUSanitization(const std::string& input) {
     41     return Locale(input);
     42 }
     43 
     44 TEST(LocaleTest, basicTests) {
     45     Locale defaultLocale;
     46     Locale emptyLocale("");
     47     Locale english = createLocale("en");
     48     Locale french = createLocale("fr");
     49     Locale und = createLocale("und");
     50     Locale undZsye = createLocale("und-Zsye");
     51 
     52     EXPECT_EQ(english, english);
     53     EXPECT_EQ(french, french);
     54 
     55     EXPECT_TRUE(defaultLocale != defaultLocale);
     56     EXPECT_TRUE(emptyLocale != emptyLocale);
     57     EXPECT_TRUE(defaultLocale != emptyLocale);
     58     EXPECT_TRUE(defaultLocale != und);
     59     EXPECT_TRUE(emptyLocale != und);
     60     EXPECT_TRUE(english != defaultLocale);
     61     EXPECT_TRUE(english != emptyLocale);
     62     EXPECT_TRUE(english != french);
     63     EXPECT_TRUE(english != undZsye);
     64     EXPECT_TRUE(und != undZsye);
     65     EXPECT_TRUE(english != und);
     66     EXPECT_TRUE(createLocale("de-1901") != createLocale("de-1996"));
     67 
     68     EXPECT_TRUE(defaultLocale.isUnsupported());
     69     EXPECT_TRUE(emptyLocale.isUnsupported());
     70 
     71     EXPECT_FALSE(english.isUnsupported());
     72     EXPECT_FALSE(french.isUnsupported());
     73     EXPECT_FALSE(und.isUnsupported());
     74     EXPECT_FALSE(undZsye.isUnsupported());
     75 }
     76 
     77 TEST(LocaleTest, getStringTest) {
     78     EXPECT_EQ("en-Latn-US", createLocale("en").getString());
     79     EXPECT_EQ("en-Latn-US", createLocale("en-Latn").getString());
     80 
     81     // Capitalized language code or lowercased script should be normalized.
     82     EXPECT_EQ("en-Latn-US", createLocale("EN-LATN").getString());
     83     EXPECT_EQ("en-Latn-US", createLocale("EN-latn").getString());
     84     EXPECT_EQ("en-Latn-US", createLocale("en-latn").getString());
     85 
     86     // Invalid script should be kept.
     87     EXPECT_EQ("en-Xyzt-US", createLocale("en-xyzt").getString());
     88 
     89     EXPECT_EQ("en-Latn-US", createLocale("en-Latn-US").getString());
     90     EXPECT_EQ("ja-Jpan-JP", createLocale("ja").getString());
     91     EXPECT_EQ("zh-Hant-TW", createLocale("zh-TW").getString());
     92     EXPECT_EQ("zh-Hant-HK", createLocale("zh-HK").getString());
     93     EXPECT_EQ("zh-Hant-MO", createLocale("zh-MO").getString());
     94     EXPECT_EQ("zh-Hans-CN", createLocale("zh").getString());
     95     EXPECT_EQ("zh-Hans-CN", createLocale("zh-CN").getString());
     96     EXPECT_EQ("zh-Hans-SG", createLocale("zh-SG").getString());
     97     EXPECT_EQ("und", createLocale("und").getString());
     98     EXPECT_EQ("und", createLocale("UND").getString());
     99     EXPECT_EQ("und", createLocale("Und").getString());
    100     EXPECT_EQ("und-Zsye", createLocale("und-Zsye").getString());
    101     EXPECT_EQ("und-Zsye", createLocale("Und-ZSYE").getString());
    102     EXPECT_EQ("und-Zsye", createLocale("Und-zsye").getString());
    103 
    104     EXPECT_EQ("es-Latn-419", createLocale("es-Latn-419").getString());
    105 
    106     // Variant
    107     EXPECT_EQ("de-Latn-DE", createLocale("de").getString());
    108     EXPECT_EQ("de-Latn-DE-1901", createLocale("de-1901").getString());
    109     EXPECT_EQ("de-Latn-DE-1996", createLocale("de-DE-1996").getString());
    110 
    111     // Line Break subtag
    112     EXPECT_EQ("ja-Jpan-JP-u-lb-loose", createLocale("ja-JP-u-lb-loose").getString());
    113     EXPECT_EQ("ja-Jpan-JP-u-lb-normal", createLocale("ja-JP-u-lb-normal").getString());
    114     EXPECT_EQ("ja-Jpan-JP-u-lb-strict", createLocale("ja-JP-u-lb-strict").getString());
    115     EXPECT_EQ("ja-Jpan-JP-u-lb-loose", createLocale("ja-JP-u-lb-loose-em-emoji").getString());
    116     EXPECT_EQ("ja-Jpan-JP-u-lb-strict", createLocale("ja-JP-u-em-default-lb-strict").getString());
    117     EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-bogus").getString());
    118 
    119     // Emoji subtag is dropped from getString().
    120     EXPECT_EQ("es-Latn-419", createLocale("es-419-u-em-emoji").getString());
    121     EXPECT_EQ("es-Latn-419", createLocale("es-Latn-419-u-em-emoji").getString());
    122 
    123     // This is not a necessary desired behavior, just known behavior.
    124     EXPECT_EQ("en-Latn-US", createLocale("und-Abcdefgh").getString());
    125 }
    126 
    127 TEST(LocaleTest, invalidLanguageTagTest) {  // just make sure no crash happens
    128     LocaleListCache::getId("ja-JP-u-lb-lb-strict");
    129 }
    130 
    131 TEST(LocaleTest, testReconstruction) {
    132     EXPECT_EQ("en", createLocaleWithoutICUSanitization("en").getString());
    133     EXPECT_EQ("fil", createLocaleWithoutICUSanitization("fil").getString());
    134     EXPECT_EQ("und", createLocaleWithoutICUSanitization("und").getString());
    135 
    136     EXPECT_EQ("en-Latn", createLocaleWithoutICUSanitization("en-Latn").getString());
    137     EXPECT_EQ("fil-Taga", createLocaleWithoutICUSanitization("fil-Taga").getString());
    138     EXPECT_EQ("und-Zsye", createLocaleWithoutICUSanitization("und-Zsye").getString());
    139 
    140     EXPECT_EQ("en-US", createLocaleWithoutICUSanitization("en-US").getString());
    141     EXPECT_EQ("fil-PH", createLocaleWithoutICUSanitization("fil-PH").getString());
    142     EXPECT_EQ("es-419", createLocaleWithoutICUSanitization("es-419").getString());
    143 
    144     EXPECT_EQ("en-Latn-US", createLocaleWithoutICUSanitization("en-Latn-US").getString());
    145     EXPECT_EQ("fil-Taga-PH", createLocaleWithoutICUSanitization("fil-Taga-PH").getString());
    146     EXPECT_EQ("es-Latn-419", createLocaleWithoutICUSanitization("es-Latn-419").getString());
    147 
    148     // Possible minimum/maximum values.
    149     EXPECT_EQ("aa", createLocaleWithoutICUSanitization("aa").getString());
    150     EXPECT_EQ("zz", createLocaleWithoutICUSanitization("zz").getString());
    151     EXPECT_EQ("aa-Aaaa", createLocaleWithoutICUSanitization("aa-Aaaa").getString());
    152     EXPECT_EQ("zz-Zzzz", createLocaleWithoutICUSanitization("zz-Zzzz").getString());
    153     EXPECT_EQ("aaa-Aaaa-AA", createLocaleWithoutICUSanitization("aaa-Aaaa-AA").getString());
    154     EXPECT_EQ("zzz-Zzzz-ZZ", createLocaleWithoutICUSanitization("zzz-Zzzz-ZZ").getString());
    155     EXPECT_EQ("aaa-Aaaa-000", createLocaleWithoutICUSanitization("aaa-Aaaa-000").getString());
    156     EXPECT_EQ("zzz-Zzzz-999", createLocaleWithoutICUSanitization("zzz-Zzzz-999").getString());
    157 }
    158 
    159 TEST(LocaleTest, ScriptEqualTest) {
    160     EXPECT_TRUE(createLocale("en").isEqualScript(createLocale("en")));
    161     EXPECT_TRUE(createLocale("en-Latn").isEqualScript(createLocale("en")));
    162     EXPECT_TRUE(createLocale("jp-Latn").isEqualScript(createLocale("en-Latn")));
    163     EXPECT_TRUE(createLocale("en-Jpan").isEqualScript(createLocale("en-Jpan")));
    164 
    165     EXPECT_FALSE(createLocale("en-Jpan").isEqualScript(createLocale("en-Hira")));
    166     EXPECT_FALSE(createLocale("en-Jpan").isEqualScript(createLocale("en-Hani")));
    167 }
    168 
    169 TEST(LocaleTest, ScriptMatchTest) {
    170     const bool SUPPORTED = true;
    171     const bool NOT_SUPPORTED = false;
    172 
    173     struct TestCase {
    174         const std::string baseScript;
    175         const std::string requestedScript;
    176         bool isSupported;
    177     } testCases[] = {
    178             // Same scripts
    179             {"en-Latn", "Latn", SUPPORTED},
    180             {"ja-Jpan", "Jpan", SUPPORTED},
    181             {"ja-Hira", "Hira", SUPPORTED},
    182             {"ja-Kana", "Kana", SUPPORTED},
    183             {"ja-Hrkt", "Hrkt", SUPPORTED},
    184             {"zh-Hans", "Hans", SUPPORTED},
    185             {"zh-Hant", "Hant", SUPPORTED},
    186             {"zh-Hani", "Hani", SUPPORTED},
    187             {"ko-Kore", "Kore", SUPPORTED},
    188             {"ko-Hang", "Hang", SUPPORTED},
    189             {"zh-Hanb", "Hanb", SUPPORTED},
    190 
    191             // Japanese supports Hiragana, Katakanara, etc.
    192             {"ja-Jpan", "Hira", SUPPORTED},
    193             {"ja-Jpan", "Kana", SUPPORTED},
    194             {"ja-Jpan", "Hrkt", SUPPORTED},
    195             {"ja-Hrkt", "Hira", SUPPORTED},
    196             {"ja-Hrkt", "Kana", SUPPORTED},
    197 
    198             // Chinese supports Han.
    199             {"zh-Hans", "Hani", SUPPORTED},
    200             {"zh-Hant", "Hani", SUPPORTED},
    201             {"zh-Hanb", "Hani", SUPPORTED},
    202 
    203             // Hanb supports Bopomofo.
    204             {"zh-Hanb", "Bopo", SUPPORTED},
    205 
    206             // Korean supports Hangul.
    207             {"ko-Kore", "Hang", SUPPORTED},
    208 
    209             // Different scripts
    210             {"ja-Jpan", "Latn", NOT_SUPPORTED},
    211             {"en-Latn", "Jpan", NOT_SUPPORTED},
    212             {"ja-Jpan", "Hant", NOT_SUPPORTED},
    213             {"zh-Hant", "Jpan", NOT_SUPPORTED},
    214             {"ja-Jpan", "Hans", NOT_SUPPORTED},
    215             {"zh-Hans", "Jpan", NOT_SUPPORTED},
    216             {"ja-Jpan", "Kore", NOT_SUPPORTED},
    217             {"ko-Kore", "Jpan", NOT_SUPPORTED},
    218             {"zh-Hans", "Hant", NOT_SUPPORTED},
    219             {"zh-Hant", "Hans", NOT_SUPPORTED},
    220             {"zh-Hans", "Kore", NOT_SUPPORTED},
    221             {"ko-Kore", "Hans", NOT_SUPPORTED},
    222             {"zh-Hant", "Kore", NOT_SUPPORTED},
    223             {"ko-Kore", "Hant", NOT_SUPPORTED},
    224 
    225             // Hiragana doesn't support Japanese, etc.
    226             {"ja-Hira", "Jpan", NOT_SUPPORTED},
    227             {"ja-Kana", "Jpan", NOT_SUPPORTED},
    228             {"ja-Hrkt", "Jpan", NOT_SUPPORTED},
    229             {"ja-Hani", "Jpan", NOT_SUPPORTED},
    230             {"ja-Hira", "Hrkt", NOT_SUPPORTED},
    231             {"ja-Kana", "Hrkt", NOT_SUPPORTED},
    232             {"ja-Hani", "Hrkt", NOT_SUPPORTED},
    233             {"ja-Hani", "Hira", NOT_SUPPORTED},
    234             {"ja-Hani", "Kana", NOT_SUPPORTED},
    235 
    236             // Kanji doesn't support Chinese, etc.
    237             {"zh-Hani", "Hant", NOT_SUPPORTED},
    238             {"zh-Hani", "Hans", NOT_SUPPORTED},
    239             {"zh-Hani", "Hanb", NOT_SUPPORTED},
    240 
    241             // Hangul doesn't support Korean, etc.
    242             {"ko-Hang", "Kore", NOT_SUPPORTED},
    243             {"ko-Hani", "Kore", NOT_SUPPORTED},
    244             {"ko-Hani", "Hang", NOT_SUPPORTED},
    245             {"ko-Hang", "Hani", NOT_SUPPORTED},
    246 
    247             // Han with botomofo doesn't support simplified Chinese, etc.
    248             {"zh-Hanb", "Hant", NOT_SUPPORTED},
    249             {"zh-Hanb", "Hans", NOT_SUPPORTED},
    250             {"zh-Hanb", "Jpan", NOT_SUPPORTED},
    251             {"zh-Hanb", "Kore", NOT_SUPPORTED},
    252     };
    253 
    254     for (const auto& testCase : testCases) {
    255         hb_script_t script = hb_script_from_iso15924_tag(
    256                 HB_TAG(testCase.requestedScript[0], testCase.requestedScript[1],
    257                        testCase.requestedScript[2], testCase.requestedScript[3]));
    258         if (testCase.isSupported) {
    259             EXPECT_TRUE(createLocale(testCase.baseScript).supportsHbScript(script))
    260                     << testCase.baseScript << " should support " << testCase.requestedScript;
    261         } else {
    262             EXPECT_FALSE(createLocale(testCase.baseScript).supportsHbScript(script))
    263                     << testCase.baseScript << " shouldn't support " << testCase.requestedScript;
    264         }
    265     }
    266 }
    267 
    268 TEST(LocaleListTest, basicTests) {
    269     LocaleList emptyLocales;
    270     EXPECT_EQ(0u, emptyLocales.size());
    271 
    272     Locale english = createLocale("en");
    273     const LocaleList& singletonLocales = createLocaleList("en");
    274     EXPECT_EQ(1u, singletonLocales.size());
    275     EXPECT_EQ(english, singletonLocales[0]);
    276 
    277     Locale french = createLocale("fr");
    278     const LocaleList& twoLocales = createLocaleList("en,fr");
    279     EXPECT_EQ(2u, twoLocales.size());
    280     EXPECT_EQ(english, twoLocales[0]);
    281     EXPECT_EQ(french, twoLocales[1]);
    282 }
    283 
    284 TEST(LocaleListTest, unsupportedLocaleuageTests) {
    285     const LocaleList& oneUnsupported = createLocaleList("abcd-example");
    286     EXPECT_TRUE(oneUnsupported.empty());
    287 
    288     const LocaleList& twoUnsupporteds = createLocaleList("abcd-example,abcd-example");
    289     EXPECT_TRUE(twoUnsupporteds.empty());
    290 
    291     Locale english = createLocale("en");
    292     const LocaleList& firstUnsupported = createLocaleList("abcd-example,en");
    293     EXPECT_EQ(1u, firstUnsupported.size());
    294     EXPECT_EQ(english, firstUnsupported[0]);
    295 
    296     const LocaleList& lastUnsupported = createLocaleList("en,abcd-example");
    297     EXPECT_EQ(1u, lastUnsupported.size());
    298     EXPECT_EQ(english, lastUnsupported[0]);
    299 }
    300 
    301 TEST(LocaleListTest, repeatedLocaleuageTests) {
    302     Locale english = createLocale("en");
    303     Locale french = createLocale("fr");
    304     Locale canadianFrench = createLocale("fr-CA");
    305     Locale englishInLatn = createLocale("en-Latn");
    306     ASSERT_TRUE(english == englishInLatn);
    307 
    308     const LocaleList& locales = createLocaleList("en,en-Latn");
    309     EXPECT_EQ(1u, locales.size());
    310     EXPECT_EQ(english, locales[0]);
    311 
    312     const LocaleList& fr = createLocaleList("fr,fr-FR,fr-Latn-FR");
    313     EXPECT_EQ(1u, fr.size());
    314     EXPECT_EQ(french, fr[0]);
    315 
    316     // ICU appends FR to fr. The third language is dropped which is same as the first language.
    317     const LocaleList& fr2 = createLocaleList("fr,fr-CA,fr-FR");
    318     EXPECT_EQ(2u, fr2.size());
    319     EXPECT_EQ(french, fr2[0]);
    320     EXPECT_EQ(canadianFrench, fr2[1]);
    321 
    322     // The order should be kept.
    323     const LocaleList& locales2 = createLocaleList("en,fr,en-Latn");
    324     EXPECT_EQ(2u, locales2.size());
    325     EXPECT_EQ(english, locales2[0]);
    326     EXPECT_EQ(french, locales2[1]);
    327 }
    328 
    329 TEST(LocaleListTest, identifierTest) {
    330     EXPECT_EQ(createLocale("en-Latn-US"), createLocale("en-Latn-US"));
    331     EXPECT_EQ(createLocale("zh-Hans-CN"), createLocale("zh-Hans-CN"));
    332     EXPECT_EQ(createLocale("en-Zsye-US"), createLocale("en-Zsye-US"));
    333 
    334     EXPECT_NE(createLocale("en-Latn-US"), createLocale("en-Latn-GB"));
    335     EXPECT_NE(createLocale("en-Latn-US"), createLocale("en-Zsye-US"));
    336     EXPECT_NE(createLocale("es-Latn-US"), createLocale("en-Latn-US"));
    337     EXPECT_NE(createLocale("zh-Hant-HK"), createLocale("zh-Hant-TW"));
    338 }
    339 
    340 TEST(LocaleListTest, undEmojiTests) {
    341     Locale emoji = createLocale("und-Zsye");
    342     EXPECT_EQ(EmojiStyle::EMOJI, emoji.getEmojiStyle());
    343 
    344     Locale und = createLocale("und");
    345     EXPECT_EQ(EmojiStyle::EMPTY, und.getEmojiStyle());
    346     EXPECT_FALSE(emoji == und);
    347 
    348     Locale undExample = createLocale("und-example");
    349     EXPECT_EQ(EmojiStyle::EMPTY, undExample.getEmojiStyle());
    350     EXPECT_FALSE(emoji == undExample);
    351 }
    352 
    353 TEST(LocaleListTest, subtagEmojiTest) {
    354     std::string subtagEmojiStrings[] = {
    355             // Duplicate subtag case.
    356             "und-Latn-u-em-emoji-u-em-text",
    357 
    358             // Strings that contain language.
    359             "und-u-em-emoji", "en-u-em-emoji",
    360 
    361             // Strings that contain the script.
    362             "und-Jpan-u-em-emoji", "en-Latn-u-em-emoji", "und-Zsym-u-em-emoji",
    363             "und-Zsye-u-em-emoji", "en-Zsym-u-em-emoji", "en-Zsye-u-em-emoji",
    364 
    365             // Strings that contain the country.
    366             "und-US-u-em-emoji", "en-US-u-em-emoji", "es-419-u-em-emoji", "und-Latn-US-u-em-emoji",
    367             "en-Zsym-US-u-em-emoji", "en-Zsye-US-u-em-emoji", "es-Zsye-419-u-em-emoji",
    368 
    369             // Strings that contain the variant.
    370             "de-Latn-DE-1901-u-em-emoji",
    371     };
    372 
    373     for (const auto& subtagEmojiString : subtagEmojiStrings) {
    374         SCOPED_TRACE("Test for \"" + subtagEmojiString + "\"");
    375         Locale subtagEmoji = createLocale(subtagEmojiString);
    376         EXPECT_EQ(EmojiStyle::EMOJI, subtagEmoji.getEmojiStyle());
    377     }
    378 }
    379 
    380 TEST(LocaleListTest, subtagTextTest) {
    381     std::string subtagTextStrings[] = {
    382             // Duplicate subtag case.
    383             "und-Latn-u-em-text-u-em-emoji",
    384 
    385             // Strings that contain language.
    386             "und-u-em-text", "en-u-em-text",
    387 
    388             // Strings that contain the script.
    389             "und-Latn-u-em-text", "en-Jpan-u-em-text", "und-Zsym-u-em-text", "und-Zsye-u-em-text",
    390             "en-Zsym-u-em-text", "en-Zsye-u-em-text",
    391 
    392             // Strings that contain the country.
    393             "und-US-u-em-text", "en-US-u-em-text", "es-419-u-em-text", "und-Latn-US-u-em-text",
    394             "en-Zsym-US-u-em-text", "en-Zsye-US-u-em-text", "es-Zsye-419-u-em-text",
    395 
    396             // Strings that contain the variant.
    397             "de-Latn-DE-1901-u-em-text",
    398     };
    399 
    400     for (const auto& subtagTextString : subtagTextStrings) {
    401         SCOPED_TRACE("Test for \"" + subtagTextString + "\"");
    402         Locale subtagText = createLocale(subtagTextString);
    403         EXPECT_EQ(EmojiStyle::TEXT, subtagText.getEmojiStyle());
    404     }
    405 }
    406 
    407 // TODO: add more "und" language cases whose language and script are
    408 //       unexpectedly translated to en-Latn by ICU.
    409 TEST(LocaleListTest, subtagDefaultTest) {
    410     std::string subtagDefaultStrings[] = {
    411             // Duplicate subtag case.
    412             "en-Latn-u-em-default-u-em-emoji", "en-Latn-u-em-default-u-em-text",
    413 
    414             // Strings that contain language.
    415             "und-u-em-default", "en-u-em-default",
    416 
    417             // Strings that contain the script.
    418             "en-Latn-u-em-default", "en-Zsym-u-em-default", "en-Zsye-u-em-default",
    419 
    420             // Strings that contain the country.
    421             "en-US-u-em-default", "en-Latn-US-u-em-default", "es-Latn-419-u-em-default",
    422             "en-Zsym-US-u-em-default", "en-Zsye-US-u-em-default", "es-Zsye-419-u-em-default",
    423 
    424             // Strings that contain the variant.
    425             "de-Latn-DE-1901-u-em-default",
    426     };
    427 
    428     for (const auto& subtagDefaultString : subtagDefaultStrings) {
    429         SCOPED_TRACE("Test for \"" + subtagDefaultString + "\"");
    430         Locale subtagDefault = createLocale(subtagDefaultString);
    431         EXPECT_EQ(EmojiStyle::DEFAULT, subtagDefault.getEmojiStyle());
    432     }
    433 }
    434 
    435 TEST(LocaleListTest, subtagEmptyTest) {
    436     std::string subtagEmptyStrings[] = {
    437             "und",
    438             "jp",
    439             "en-US",
    440             "en-Latn",
    441             "en-Latn-US",
    442             "en-Latn-US-u-em",
    443             "en-Latn-US-u-em-defaultemoji",
    444             "de-Latn-DE-1901",
    445     };
    446 
    447     for (const auto& subtagEmptyString : subtagEmptyStrings) {
    448         SCOPED_TRACE("Test for \"" + subtagEmptyString + "\"");
    449         Locale subtagEmpty = createLocale(subtagEmptyString);
    450         EXPECT_EQ(EmojiStyle::EMPTY, subtagEmpty.getEmojiStyle());
    451     }
    452 }
    453 
    454 TEST(LocaleListTest, registerLocaleListTest) {
    455     EXPECT_EQ(0UL, registerLocaleList(""));
    456     EXPECT_NE(0UL, registerLocaleList("en"));
    457     EXPECT_NE(0UL, registerLocaleList("jp"));
    458     EXPECT_NE(0UL, registerLocaleList("en,zh-Hans"));
    459 
    460     EXPECT_EQ(registerLocaleList("en"), registerLocaleList("en"));
    461     EXPECT_NE(registerLocaleList("en"), registerLocaleList("jp"));
    462     EXPECT_NE(registerLocaleList("de"), registerLocaleList("de-1901"));
    463 
    464     EXPECT_EQ(registerLocaleList("en,zh-Hans"), registerLocaleList("en,zh-Hans"));
    465     EXPECT_NE(registerLocaleList("en,zh-Hans"), registerLocaleList("zh-Hans,en"));
    466     EXPECT_NE(registerLocaleList("en,zh-Hans"), registerLocaleList("jp"));
    467     EXPECT_NE(registerLocaleList("en,zh-Hans"), registerLocaleList("en"));
    468     EXPECT_NE(registerLocaleList("en,zh-Hans"), registerLocaleList("en,zh-Hant"));
    469     EXPECT_NE(registerLocaleList("de,de-1901"), registerLocaleList("de-1901,de"));
    470 }
    471 
    472 // The test font has following glyphs.
    473 // U+82A6
    474 // U+82A6 U+FE00 (VS1)
    475 // U+82A6 U+E0100 (VS17)
    476 // U+82A6 U+E0101 (VS18)
    477 // U+82A6 U+E0102 (VS19)
    478 // U+845B
    479 // U+845B U+FE00 (VS2)
    480 // U+845B U+E0101 (VS18)
    481 // U+845B U+E0102 (VS19)
    482 // U+845B U+E0103 (VS20)
    483 // U+537F
    484 // U+717D U+FE02 (VS3)
    485 // U+717D U+E0102 (VS19)
    486 // U+717D U+E0103 (VS20)
    487 const char kVsTestFont[] = "VariationSelectorTest-Regular.ttf";
    488 
    489 class FontFamilyTest : public testing::Test {
    490 public:
    491     virtual void SetUp() override {
    492         if (access(getTestFontPath(kVsTestFont).c_str(), R_OK) != 0) {
    493             FAIL() << "Unable to read " << kVsTestFont << ". "
    494                    << "Please prepare the test data directory. "
    495                    << "For more details, please see how_to_run.txt.";
    496         }
    497     }
    498 };
    499 
    500 // Asserts that the font family has glyphs for and only for specified codepoint
    501 // and variationSelector pairs.
    502 void expectVSGlyphs(FontFamily* family, uint32_t codepoint, const std::set<uint32_t>& vs) {
    503     for (uint32_t i = 0xFE00; i <= 0xE01EF; ++i) {
    504         // Move to variation selectors supplements after variation selectors.
    505         if (i == 0xFF00) {
    506             i = 0xE0100;
    507         }
    508         if (vs.find(i) == vs.end()) {
    509             EXPECT_FALSE(family->hasGlyph(codepoint, i))
    510                     << "Glyph for U+" << std::hex << codepoint << " U+" << i;
    511         } else {
    512             EXPECT_TRUE(family->hasGlyph(codepoint, i))
    513                     << "Glyph for U+" << std::hex << codepoint << " U+" << i;
    514         }
    515     }
    516 }
    517 
    518 TEST_F(FontFamilyTest, hasVariationSelectorTest) {
    519     std::shared_ptr<FontFamily> family = buildFontFamily(kVsTestFont);
    520 
    521     const uint32_t kVS1 = 0xFE00;
    522     const uint32_t kVS2 = 0xFE01;
    523     const uint32_t kVS3 = 0xFE02;
    524     const uint32_t kVS17 = 0xE0100;
    525     const uint32_t kVS18 = 0xE0101;
    526     const uint32_t kVS19 = 0xE0102;
    527     const uint32_t kVS20 = 0xE0103;
    528 
    529     const uint32_t kSupportedChar1 = 0x82A6;
    530     EXPECT_TRUE(family->getCoverage().get(kSupportedChar1));
    531     expectVSGlyphs(family.get(), kSupportedChar1, std::set<uint32_t>({kVS1, kVS17, kVS18, kVS19}));
    532 
    533     const uint32_t kSupportedChar2 = 0x845B;
    534     EXPECT_TRUE(family->getCoverage().get(kSupportedChar2));
    535     expectVSGlyphs(family.get(), kSupportedChar2, std::set<uint32_t>({kVS2, kVS18, kVS19, kVS20}));
    536 
    537     const uint32_t kNoVsSupportedChar = 0x537F;
    538     EXPECT_TRUE(family->getCoverage().get(kNoVsSupportedChar));
    539     expectVSGlyphs(family.get(), kNoVsSupportedChar, std::set<uint32_t>());
    540 
    541     const uint32_t kVsOnlySupportedChar = 0x717D;
    542     EXPECT_FALSE(family->getCoverage().get(kVsOnlySupportedChar));
    543     expectVSGlyphs(family.get(), kVsOnlySupportedChar, std::set<uint32_t>({kVS3, kVS19, kVS20}));
    544 
    545     const uint32_t kNotSupportedChar = 0x845C;
    546     EXPECT_FALSE(family->getCoverage().get(kNotSupportedChar));
    547     expectVSGlyphs(family.get(), kNotSupportedChar, std::set<uint32_t>());
    548 }
    549 
    550 TEST_F(FontFamilyTest, hasVSTableTest) {
    551     struct TestCase {
    552         const std::string fontPath;
    553         bool hasVSTable;
    554     } testCases[] = {
    555             {"Ja.ttf", true},      {"ZhHant.ttf", true}, {"ZhHans.ttf", true},
    556             {"Italic.ttf", false}, {"Bold.ttf", false},  {"BoldItalic.ttf", false},
    557     };
    558 
    559     for (const auto& testCase : testCases) {
    560         SCOPED_TRACE(testCase.hasVSTable ? "Font " + testCase.fontPath +
    561                                                    " should have a variation sequence table."
    562                                          : "Font " + testCase.fontPath +
    563                                                    " shouldn't have a variation sequence table.");
    564 
    565         std::shared_ptr<FontFamily> family = buildFontFamily(testCase.fontPath);
    566         EXPECT_EQ(testCase.hasVSTable, family->hasVSTable());
    567     }
    568 }
    569 
    570 TEST_F(FontFamilyTest, createFamilyWithVariationTest) {
    571     // This font has 'wdth' and 'wght' axes.
    572     const char kMultiAxisFont[] = "MultiAxis.ttf";
    573     const char kNoAxisFont[] = "Regular.ttf";
    574 
    575     std::shared_ptr<FontFamily> multiAxisFamily = buildFontFamily(kMultiAxisFont);
    576     std::shared_ptr<FontFamily> noAxisFamily = buildFontFamily(kNoAxisFont);
    577 
    578     {
    579         // Do not ceate new instance if none of variations are specified.
    580         EXPECT_EQ(nullptr,
    581                   multiAxisFamily->createFamilyWithVariation(std::vector<FontVariation>()));
    582         EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(std::vector<FontVariation>()));
    583     }
    584     {
    585         // New instance should be used for supported variation.
    586         std::vector<FontVariation> variations = {{MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f}};
    587         std::shared_ptr<FontFamily> newFamily(
    588                 multiAxisFamily->createFamilyWithVariation(variations));
    589         EXPECT_NE(nullptr, newFamily.get());
    590         EXPECT_NE(multiAxisFamily.get(), newFamily.get());
    591         EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
    592     }
    593     {
    594         // New instance should be used for supported variation. (multiple variations case)
    595         std::vector<FontVariation> variations = {{MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f},
    596                                                  {MinikinFont::MakeTag('w', 'g', 'h', 't'), 1.0f}};
    597         std::shared_ptr<FontFamily> newFamily(
    598                 multiAxisFamily->createFamilyWithVariation(variations));
    599         EXPECT_NE(nullptr, newFamily.get());
    600         EXPECT_NE(multiAxisFamily.get(), newFamily.get());
    601         EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
    602     }
    603     {
    604         // Do not ceate new instance if none of variations are supported.
    605         std::vector<FontVariation> variations = {{MinikinFont::MakeTag('Z', 'Z', 'Z', 'Z'), 1.0f}};
    606         EXPECT_EQ(nullptr, multiAxisFamily->createFamilyWithVariation(variations));
    607         EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
    608     }
    609     {
    610         // At least one axis is supported, should create new instance.
    611         std::vector<FontVariation> variations = {{MinikinFont::MakeTag('w', 'd', 't', 'h'), 1.0f},
    612                                                  {MinikinFont::MakeTag('Z', 'Z', 'Z', 'Z'), 1.0f}};
    613         std::shared_ptr<FontFamily> newFamily(
    614                 multiAxisFamily->createFamilyWithVariation(variations));
    615         EXPECT_NE(nullptr, newFamily.get());
    616         EXPECT_NE(multiAxisFamily.get(), newFamily.get());
    617         EXPECT_EQ(nullptr, noAxisFamily->createFamilyWithVariation(variations));
    618     }
    619 }
    620 
    621 TEST_F(FontFamilyTest, coverageTableSelectionTest) {
    622     // This font supports U+0061. The cmap subtable is format 4 and its platform ID is 0 and
    623     // encoding ID is 1.
    624     const char kUnicodeEncoding1Font[] = "UnicodeBMPOnly.ttf";
    625 
    626     // This font supports U+0061. The cmap subtable is format 4 and its platform ID is 0 and
    627     // encoding ID is 3.
    628     const char kUnicodeEncoding3Font[] = "UnicodeBMPOnly2.ttf";
    629 
    630     // This font has both cmap format 4 subtable which platform ID is 0 and encoding ID is 1
    631     // and cmap format 14 subtable which platform ID is 0 and encoding ID is 10.
    632     // U+0061 is listed in both subtable but U+1F926 is only listed in latter.
    633     const char kUnicodeEncoding4Font[] = "UnicodeUCS4.ttf";
    634 
    635     std::shared_ptr<FontFamily> unicodeEnc1Font = buildFontFamily(kUnicodeEncoding1Font);
    636     std::shared_ptr<FontFamily> unicodeEnc3Font = buildFontFamily(kUnicodeEncoding3Font);
    637     std::shared_ptr<FontFamily> unicodeEnc4Font = buildFontFamily(kUnicodeEncoding4Font);
    638 
    639     EXPECT_TRUE(unicodeEnc1Font->hasGlyph(0x0061, 0));
    640     EXPECT_TRUE(unicodeEnc3Font->hasGlyph(0x0061, 0));
    641     EXPECT_TRUE(unicodeEnc4Font->hasGlyph(0x0061, 0));
    642 
    643     EXPECT_TRUE(unicodeEnc4Font->hasGlyph(0x1F926, 0));
    644 }
    645 
    646 const char* slantToString(FontStyle::Slant slant) {
    647     if (slant == FontStyle::Slant::ITALIC) {
    648         return "ITALIC";
    649     } else {
    650         return "UPRIGHT";
    651     }
    652 }
    653 
    654 std::string fontStyleToString(const FontStyle& style) {
    655     char buf[64] = {};
    656     snprintf(buf, sizeof(buf), "FontStyle(weight=%d, slant=%s)", style.weight(),
    657              slantToString(style.slant()));
    658     return buf;
    659 }
    660 
    661 TEST_F(FontFamilyTest, closestMatch) {
    662     constexpr char kTestFont[] = "Ascii.ttf";
    663 
    664     constexpr FontStyle::Weight THIN = FontStyle::Weight::THIN;
    665     constexpr FontStyle::Weight LIGHT = FontStyle::Weight::LIGHT;
    666     constexpr FontStyle::Weight NORMAL = FontStyle::Weight::NORMAL;
    667     constexpr FontStyle::Weight MEDIUM = FontStyle::Weight::MEDIUM;
    668     constexpr FontStyle::Weight BOLD = FontStyle::Weight::BOLD;
    669     constexpr FontStyle::Weight BLACK = FontStyle::Weight::BLACK;
    670 
    671     constexpr FontStyle::Slant UPRIGHT = FontStyle::Slant::UPRIGHT;
    672     constexpr FontStyle::Slant ITALIC = FontStyle::Slant::ITALIC;
    673 
    674     const std::vector<FontStyle> STANDARD_SET = {
    675             FontStyle(NORMAL, UPRIGHT),  // 0
    676             FontStyle(BOLD, UPRIGHT),    // 1
    677             FontStyle(NORMAL, ITALIC),   // 2
    678             FontStyle(BOLD, ITALIC),     // 3
    679     };
    680 
    681     const std::vector<FontStyle> FULL_SET = {
    682             FontStyle(THIN, UPRIGHT),    // 0
    683             FontStyle(LIGHT, UPRIGHT),   // 1
    684             FontStyle(NORMAL, UPRIGHT),  // 2
    685             FontStyle(MEDIUM, UPRIGHT),  // 3
    686             FontStyle(BOLD, UPRIGHT),    // 4
    687             FontStyle(BLACK, UPRIGHT),   // 5
    688             FontStyle(THIN, ITALIC),     // 6
    689             FontStyle(LIGHT, ITALIC),    // 7
    690             FontStyle(NORMAL, ITALIC),   // 8
    691             FontStyle(MEDIUM, ITALIC),   // 9
    692             FontStyle(BOLD, ITALIC),     // 10
    693             FontStyle(BLACK, ITALIC),    // 11
    694     };
    695     struct TestCase {
    696         FontStyle wantedStyle;
    697         std::vector<FontStyle> familyStyles;
    698         size_t expectedIndex;
    699     } testCases[] = {
    700             {FontStyle(), {FontStyle()}, 0},
    701 
    702             // Exact matches
    703             {FontStyle(BOLD), {FontStyle(NORMAL), FontStyle(BOLD)}, 1},
    704             {FontStyle(BOLD), {FontStyle(LIGHT), FontStyle(BOLD)}, 1},
    705             {FontStyle(LIGHT), {FontStyle(NORMAL), FontStyle(LIGHT)}, 1},
    706             {FontStyle(LIGHT), {FontStyle(BOLD), FontStyle(LIGHT)}, 1},
    707             {FontStyle(NORMAL), {FontStyle(NORMAL), FontStyle(LIGHT)}, 0},
    708             {FontStyle(NORMAL), {FontStyle(NORMAL), FontStyle(BOLD)}, 0},
    709             {FontStyle(LIGHT), {FontStyle(LIGHT), FontStyle(NORMAL), FontStyle(BOLD)}, 0},
    710             {FontStyle(NORMAL), {FontStyle(LIGHT), FontStyle(NORMAL), FontStyle(BOLD)}, 1},
    711             {FontStyle(BOLD), {FontStyle(LIGHT), FontStyle(NORMAL), FontStyle(BOLD)}, 2},
    712 
    713             {FontStyle(UPRIGHT), {FontStyle(UPRIGHT), FontStyle(ITALIC)}, 0},
    714             {FontStyle(ITALIC), {FontStyle(UPRIGHT), FontStyle(ITALIC)}, 1},
    715 
    716             {FontStyle(NORMAL, UPRIGHT), STANDARD_SET, 0},
    717             {FontStyle(BOLD, UPRIGHT), STANDARD_SET, 1},
    718             {FontStyle(NORMAL, ITALIC), STANDARD_SET, 2},
    719             {FontStyle(BOLD, ITALIC), STANDARD_SET, 3},
    720 
    721             {FontStyle(NORMAL, UPRIGHT), FULL_SET, 2},
    722             {FontStyle(BOLD, UPRIGHT), FULL_SET, 4},
    723             {FontStyle(NORMAL, ITALIC), FULL_SET, 8},
    724             {FontStyle(BOLD, ITALIC), FULL_SET, 10},
    725 
    726             // TODO: Add fallback expectations. (b/68814338)
    727     };
    728 
    729     for (const TestCase& testCase : testCases) {
    730         std::vector<std::shared_ptr<MinikinFont>> dummyFonts;
    731         std::vector<Font> fonts;
    732         for (auto familyStyle : testCase.familyStyles) {
    733             std::shared_ptr<MinikinFont> dummyFont(
    734                     new FreeTypeMinikinFontForTest(getTestFontPath(kTestFont)));
    735             dummyFonts.push_back(dummyFont);
    736             fonts.push_back(Font::Builder(dummyFont).setStyle(familyStyle).build());
    737         }
    738 
    739         FontFamily family(std::move(fonts));
    740         FakedFont closest = family.getClosestMatch(testCase.wantedStyle);
    741 
    742         size_t idx = dummyFonts.size();
    743         for (size_t i = 0; i < dummyFonts.size(); i++) {
    744             if (dummyFonts[i].get() == closest.font->typeface().get()) {
    745                 idx = i;
    746                 break;
    747             }
    748         }
    749         ASSERT_NE(idx, dummyFonts.size()) << "The selected font is unknown.";
    750         EXPECT_EQ(testCase.expectedIndex, idx)
    751                 << "Input Style: " << fontStyleToString(testCase.wantedStyle) << std::endl
    752                 << "Actual Families' Style: " << fontStyleToString(testCase.familyStyles[idx])
    753                 << std::endl
    754                 << "Expected Families' Style: "
    755                 << fontStyleToString(testCase.familyStyles[testCase.expectedIndex]) << std::endl;
    756     }
    757 }
    758 
    759 }  // namespace minikin
    760