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