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