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 <gtest/gtest.h>
18 
19 #include <minikin/FontFamily.h>
20 
21 #include <cutils/log.h>
22 
23 #include "FontLanguageListCache.h"
24 #include "ICUTestBase.h"
25 #include "MinikinFontForTest.h"
26 #include "MinikinInternal.h"
27 
28 namespace android {
29 
30 typedef ICUTestBase FontLanguagesTest;
31 typedef ICUTestBase FontLanguageTest;
32 
createFontLanguages(const std::string & input)33 static const FontLanguages& createFontLanguages(const std::string& input) {
34     AutoMutex _l(gMinikinLock);
35     uint32_t langId = FontLanguageListCache::getId(input);
36     return FontLanguageListCache::getById(langId);
37 }
38 
createFontLanguage(const std::string & input)39 static FontLanguage createFontLanguage(const std::string& input) {
40     AutoMutex _l(gMinikinLock);
41     uint32_t langId = FontLanguageListCache::getId(input);
42     return FontLanguageListCache::getById(langId)[0];
43 }
44 
TEST_F(FontLanguageTest,basicTests)45 TEST_F(FontLanguageTest, basicTests) {
46     FontLanguage defaultLang;
47     FontLanguage emptyLang("", 0);
48     FontLanguage english = createFontLanguage("en");
49     FontLanguage french = createFontLanguage("fr");
50     FontLanguage und = createFontLanguage("und");
51     FontLanguage undZsye = createFontLanguage("und-Zsye");
52 
53     EXPECT_EQ(english, english);
54     EXPECT_EQ(french, french);
55 
56     EXPECT_TRUE(defaultLang != defaultLang);
57     EXPECT_TRUE(emptyLang != emptyLang);
58     EXPECT_TRUE(defaultLang != emptyLang);
59     EXPECT_TRUE(defaultLang != und);
60     EXPECT_TRUE(emptyLang != und);
61     EXPECT_TRUE(english != defaultLang);
62     EXPECT_TRUE(english != emptyLang);
63     EXPECT_TRUE(english != french);
64     EXPECT_TRUE(english != undZsye);
65     EXPECT_TRUE(und != undZsye);
66     EXPECT_TRUE(english != und);
67 
68     EXPECT_TRUE(defaultLang.isUnsupported());
69     EXPECT_TRUE(emptyLang.isUnsupported());
70 
71     EXPECT_FALSE(english.isUnsupported());
72     EXPECT_FALSE(french.isUnsupported());
73     EXPECT_FALSE(und.isUnsupported());
74     EXPECT_FALSE(undZsye.isUnsupported());
75 }
76 
TEST_F(FontLanguageTest,getStringTest)77 TEST_F(FontLanguageTest, getStringTest) {
78     EXPECT_EQ("en-Latn", createFontLanguage("en").getString());
79     EXPECT_EQ("en-Latn", createFontLanguage("en-Latn").getString());
80 
81     // Capitalized language code or lowercased script should be normalized.
82     EXPECT_EQ("en-Latn", createFontLanguage("EN-LATN").getString());
83     EXPECT_EQ("en-Latn", createFontLanguage("EN-latn").getString());
84     EXPECT_EQ("en-Latn", createFontLanguage("en-latn").getString());
85 
86     // Invalid script should be kept.
87     EXPECT_EQ("en-Xyzt", createFontLanguage("en-xyzt").getString());
88 
89     EXPECT_EQ("en-Latn", createFontLanguage("en-Latn-US").getString());
90     EXPECT_EQ("ja-Jpan", createFontLanguage("ja").getString());
91     EXPECT_EQ("und", createFontLanguage("und").getString());
92     EXPECT_EQ("und", createFontLanguage("UND").getString());
93     EXPECT_EQ("und", createFontLanguage("Und").getString());
94     EXPECT_EQ("und-Zsye", createFontLanguage("und-Zsye").getString());
95     EXPECT_EQ("und-Zsye", createFontLanguage("Und-ZSYE").getString());
96     EXPECT_EQ("und-Zsye", createFontLanguage("Und-zsye").getString());
97 
98     EXPECT_EQ("de-Latn", createFontLanguage("de-1901").getString());
99 
100     // This is not a necessary desired behavior, just known behavior.
101     EXPECT_EQ("en-Latn", createFontLanguage("und-Abcdefgh").getString());
102 }
103 
TEST_F(FontLanguageTest,ScriptEqualTest)104 TEST_F(FontLanguageTest, ScriptEqualTest) {
105     EXPECT_TRUE(createFontLanguage("en").isEqualScript(createFontLanguage("en")));
106     EXPECT_TRUE(createFontLanguage("en-Latn").isEqualScript(createFontLanguage("en")));
107     EXPECT_TRUE(createFontLanguage("jp-Latn").isEqualScript(createFontLanguage("en-Latn")));
108     EXPECT_TRUE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Jpan")));
109 
110     EXPECT_FALSE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Hira")));
111     EXPECT_FALSE(createFontLanguage("en-Jpan").isEqualScript(createFontLanguage("en-Hani")));
112 }
113 
TEST_F(FontLanguageTest,ScriptMatchTest)114 TEST_F(FontLanguageTest, ScriptMatchTest) {
115     const bool SUPPORTED = true;
116     const bool NOT_SUPPORTED = false;
117 
118     struct TestCase {
119         const std::string baseScript;
120         const std::string requestedScript;
121         bool isSupported;
122     } testCases[] = {
123         // Same scripts
124         { "en-Latn", "Latn", SUPPORTED },
125         { "ja-Jpan", "Jpan", SUPPORTED },
126         { "ja-Hira", "Hira", SUPPORTED },
127         { "ja-Kana", "Kana", SUPPORTED },
128         { "ja-Hrkt", "Hrkt", SUPPORTED },
129         { "zh-Hans", "Hans", SUPPORTED },
130         { "zh-Hant", "Hant", SUPPORTED },
131         { "zh-Hani", "Hani", SUPPORTED },
132         { "ko-Kore", "Kore", SUPPORTED },
133         { "ko-Hang", "Hang", SUPPORTED },
134         { "zh-Hanb", "Hanb", SUPPORTED },
135 
136         // Japanese supports Hiragana, Katakanara, etc.
137         { "ja-Jpan", "Hira", SUPPORTED },
138         { "ja-Jpan", "Kana", SUPPORTED },
139         { "ja-Jpan", "Hrkt", SUPPORTED },
140         { "ja-Hrkt", "Hira", SUPPORTED },
141         { "ja-Hrkt", "Kana", SUPPORTED },
142 
143         // Chinese supports Han.
144         { "zh-Hans", "Hani", SUPPORTED },
145         { "zh-Hant", "Hani", SUPPORTED },
146         { "zh-Hanb", "Hani", SUPPORTED },
147 
148         // Hanb supports Bopomofo.
149         { "zh-Hanb", "Bopo", SUPPORTED },
150 
151         // Korean supports Hangul.
152         { "ko-Kore", "Hang", SUPPORTED },
153 
154         // Different scripts
155         { "ja-Jpan", "Latn", NOT_SUPPORTED },
156         { "en-Latn", "Jpan", NOT_SUPPORTED },
157         { "ja-Jpan", "Hant", NOT_SUPPORTED },
158         { "zh-Hant", "Jpan", NOT_SUPPORTED },
159         { "ja-Jpan", "Hans", NOT_SUPPORTED },
160         { "zh-Hans", "Jpan", NOT_SUPPORTED },
161         { "ja-Jpan", "Kore", NOT_SUPPORTED },
162         { "ko-Kore", "Jpan", NOT_SUPPORTED },
163         { "zh-Hans", "Hant", NOT_SUPPORTED },
164         { "zh-Hant", "Hans", NOT_SUPPORTED },
165         { "zh-Hans", "Kore", NOT_SUPPORTED },
166         { "ko-Kore", "Hans", NOT_SUPPORTED },
167         { "zh-Hant", "Kore", NOT_SUPPORTED },
168         { "ko-Kore", "Hant", NOT_SUPPORTED },
169 
170         // Hiragana doesn't support Japanese, etc.
171         { "ja-Hira", "Jpan", NOT_SUPPORTED },
172         { "ja-Kana", "Jpan", NOT_SUPPORTED },
173         { "ja-Hrkt", "Jpan", NOT_SUPPORTED },
174         { "ja-Hani", "Jpan", NOT_SUPPORTED },
175         { "ja-Hira", "Hrkt", NOT_SUPPORTED },
176         { "ja-Kana", "Hrkt", NOT_SUPPORTED },
177         { "ja-Hani", "Hrkt", NOT_SUPPORTED },
178         { "ja-Hani", "Hira", NOT_SUPPORTED },
179         { "ja-Hani", "Kana", NOT_SUPPORTED },
180 
181         // Kanji doesn't support Chinese, etc.
182         { "zh-Hani", "Hant", NOT_SUPPORTED },
183         { "zh-Hani", "Hans", NOT_SUPPORTED },
184         { "zh-Hani", "Hanb", NOT_SUPPORTED },
185 
186         // Hangul doesn't support Korean, etc.
187         { "ko-Hang", "Kore", NOT_SUPPORTED },
188         { "ko-Hani", "Kore", NOT_SUPPORTED },
189         { "ko-Hani", "Hang", NOT_SUPPORTED },
190         { "ko-Hang", "Hani", NOT_SUPPORTED },
191 
192         // Han with botomofo doesn't support simplified Chinese, etc.
193         { "zh-Hanb", "Hant", NOT_SUPPORTED },
194         { "zh-Hanb", "Hans", NOT_SUPPORTED },
195         { "zh-Hanb", "Jpan", NOT_SUPPORTED },
196         { "zh-Hanb", "Kore", NOT_SUPPORTED },
197     };
198 
199     for (auto testCase : testCases) {
200         hb_script_t script = hb_script_from_iso15924_tag(
201                 HB_TAG(testCase.requestedScript[0], testCase.requestedScript[1],
202                        testCase.requestedScript[2], testCase.requestedScript[3]));
203         if (testCase.isSupported) {
204             EXPECT_TRUE(
205                     createFontLanguage(testCase.baseScript).supportsHbScript(script))
206                     << testCase.baseScript << " should support " << testCase.requestedScript;
207         } else {
208             EXPECT_FALSE(
209                     createFontLanguage(testCase.baseScript).supportsHbScript(script))
210                     << testCase.baseScript << " shouldn't support " << testCase.requestedScript;
211         }
212     }
213 }
214 
TEST_F(FontLanguagesTest,basicTests)215 TEST_F(FontLanguagesTest, basicTests) {
216     FontLanguages emptyLangs;
217     EXPECT_EQ(0u, emptyLangs.size());
218 
219     FontLanguage english = createFontLanguage("en");
220     const FontLanguages& singletonLangs = createFontLanguages("en");
221     EXPECT_EQ(1u, singletonLangs.size());
222     EXPECT_EQ(english, singletonLangs[0]);
223 
224     FontLanguage french = createFontLanguage("fr");
225     const FontLanguages& twoLangs = createFontLanguages("en,fr");
226     EXPECT_EQ(2u, twoLangs.size());
227     EXPECT_EQ(english, twoLangs[0]);
228     EXPECT_EQ(french, twoLangs[1]);
229 }
230 
TEST_F(FontLanguagesTest,unsupportedLanguageTests)231 TEST_F(FontLanguagesTest, unsupportedLanguageTests) {
232     const FontLanguages& oneUnsupported = createFontLanguages("abcd-example");
233     EXPECT_TRUE(oneUnsupported.empty());
234 
235     const FontLanguages& twoUnsupporteds = createFontLanguages("abcd-example,abcd-example");
236     EXPECT_TRUE(twoUnsupporteds.empty());
237 
238     FontLanguage english = createFontLanguage("en");
239     const FontLanguages& firstUnsupported = createFontLanguages("abcd-example,en");
240     EXPECT_EQ(1u, firstUnsupported.size());
241     EXPECT_EQ(english, firstUnsupported[0]);
242 
243     const FontLanguages& lastUnsupported = createFontLanguages("en,abcd-example");
244     EXPECT_EQ(1u, lastUnsupported.size());
245     EXPECT_EQ(english, lastUnsupported[0]);
246 }
247 
TEST_F(FontLanguagesTest,repeatedLanguageTests)248 TEST_F(FontLanguagesTest, repeatedLanguageTests) {
249     FontLanguage english = createFontLanguage("en");
250     FontLanguage french = createFontLanguage("fr");
251     FontLanguage englishInLatn = createFontLanguage("en-Latn");
252     ASSERT_TRUE(english == englishInLatn);
253 
254     const FontLanguages& langs = createFontLanguages("en,en-Latn");
255     EXPECT_EQ(1u, langs.size());
256     EXPECT_EQ(english, langs[0]);
257 
258     // Country codes are ignored.
259     const FontLanguages& fr = createFontLanguages("fr,fr-CA,fr-FR");
260     EXPECT_EQ(1u, fr.size());
261     EXPECT_EQ(french, fr[0]);
262 
263     // The order should be kept.
264     const FontLanguages& langs2 = createFontLanguages("en,fr,en-Latn");
265     EXPECT_EQ(2u, langs2.size());
266     EXPECT_EQ(english, langs2[0]);
267     EXPECT_EQ(french, langs2[1]);
268 }
269 
TEST_F(FontLanguagesTest,undEmojiTests)270 TEST_F(FontLanguagesTest, undEmojiTests) {
271     FontLanguage emoji = createFontLanguage("und-Zsye");
272     EXPECT_TRUE(emoji.hasEmojiFlag());
273 
274     FontLanguage und = createFontLanguage("und");
275     EXPECT_FALSE(und.hasEmojiFlag());
276     EXPECT_FALSE(emoji == und);
277 
278     FontLanguage undExample = createFontLanguage("und-example");
279     EXPECT_FALSE(undExample.hasEmojiFlag());
280     EXPECT_FALSE(emoji == undExample);
281 }
282 
TEST_F(FontLanguagesTest,registerLanguageListTest)283 TEST_F(FontLanguagesTest, registerLanguageListTest) {
284     EXPECT_EQ(0UL, FontStyle::registerLanguageList(""));
285     EXPECT_NE(0UL, FontStyle::registerLanguageList("en"));
286     EXPECT_NE(0UL, FontStyle::registerLanguageList("jp"));
287     EXPECT_NE(0UL, FontStyle::registerLanguageList("en,zh-Hans"));
288 
289     EXPECT_EQ(FontStyle::registerLanguageList("en"), FontStyle::registerLanguageList("en"));
290     EXPECT_NE(FontStyle::registerLanguageList("en"), FontStyle::registerLanguageList("jp"));
291 
292     EXPECT_EQ(FontStyle::registerLanguageList("en,zh-Hans"),
293               FontStyle::registerLanguageList("en,zh-Hans"));
294     EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
295               FontStyle::registerLanguageList("zh-Hans,en"));
296     EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
297               FontStyle::registerLanguageList("jp"));
298     EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
299               FontStyle::registerLanguageList("en"));
300     EXPECT_NE(FontStyle::registerLanguageList("en,zh-Hans"),
301               FontStyle::registerLanguageList("en,zh-Hant"));
302 }
303 
304 // The test font has following glyphs.
305 // U+82A6
306 // U+82A6 U+FE00 (VS1)
307 // U+82A6 U+E0100 (VS17)
308 // U+82A6 U+E0101 (VS18)
309 // U+82A6 U+E0102 (VS19)
310 // U+845B
311 // U+845B U+FE00 (VS2)
312 // U+845B U+E0101 (VS18)
313 // U+845B U+E0102 (VS19)
314 // U+845B U+E0103 (VS20)
315 // U+537F
316 // U+717D U+FE02 (VS3)
317 // U+717D U+E0102 (VS19)
318 // U+717D U+E0103 (VS20)
319 const char kVsTestFont[] = kTestFontDir "VarioationSelectorTest-Regular.ttf";
320 
321 class FontFamilyTest : public ICUTestBase {
322 public:
SetUp()323     virtual void SetUp() override {
324         ICUTestBase::SetUp();
325         if (access(kVsTestFont, R_OK) != 0) {
326             FAIL() << "Unable to read " << kVsTestFont << ". "
327                    << "Please prepare the test data directory. "
328                    << "For more details, please see how_to_run.txt.";
329         }
330     }
331 };
332 
333 // Asserts that the font family has glyphs for and only for specified codepoint
334 // and variationSelector pairs.
expectVSGlyphs(FontFamily * family,uint32_t codepoint,const std::set<uint32_t> & vs)335 void expectVSGlyphs(FontFamily* family, uint32_t codepoint, const std::set<uint32_t>& vs) {
336     for (uint32_t i = 0xFE00; i <= 0xE01EF; ++i) {
337         // Move to variation selectors supplements after variation selectors.
338         if (i == 0xFF00) {
339             i = 0xE0100;
340         }
341         if (vs.find(i) == vs.end()) {
342             EXPECT_FALSE(family->hasGlyph(codepoint, i))
343                     << "Glyph for U+" << std::hex << codepoint << " U+" << i;
344         } else {
345             EXPECT_TRUE(family->hasGlyph(codepoint, i))
346                     << "Glyph for U+" << std::hex << codepoint << " U+" << i;
347         }
348 
349     }
350 }
351 
TEST_F(FontFamilyTest,hasVariationSelectorTest)352 TEST_F(FontFamilyTest, hasVariationSelectorTest) {
353     MinikinAutoUnref<MinikinFontForTest> minikinFont(new MinikinFontForTest(kVsTestFont));
354     MinikinAutoUnref<FontFamily> family(new FontFamily);
355     family->addFont(minikinFont.get());
356 
357     AutoMutex _l(gMinikinLock);
358 
359     const uint32_t kVS1 = 0xFE00;
360     const uint32_t kVS2 = 0xFE01;
361     const uint32_t kVS3 = 0xFE02;
362     const uint32_t kVS17 = 0xE0100;
363     const uint32_t kVS18 = 0xE0101;
364     const uint32_t kVS19 = 0xE0102;
365     const uint32_t kVS20 = 0xE0103;
366 
367     const uint32_t kSupportedChar1 = 0x82A6;
368     EXPECT_TRUE(family->getCoverage()->get(kSupportedChar1));
369     expectVSGlyphs(family.get(), kSupportedChar1, std::set<uint32_t>({kVS1, kVS17, kVS18, kVS19}));
370 
371     const uint32_t kSupportedChar2 = 0x845B;
372     EXPECT_TRUE(family->getCoverage()->get(kSupportedChar2));
373     expectVSGlyphs(family.get(), kSupportedChar2, std::set<uint32_t>({kVS2, kVS18, kVS19, kVS20}));
374 
375     const uint32_t kNoVsSupportedChar = 0x537F;
376     EXPECT_TRUE(family->getCoverage()->get(kNoVsSupportedChar));
377     expectVSGlyphs(family.get(), kNoVsSupportedChar, std::set<uint32_t>());
378 
379     const uint32_t kVsOnlySupportedChar = 0x717D;
380     EXPECT_FALSE(family->getCoverage()->get(kVsOnlySupportedChar));
381     expectVSGlyphs(family.get(), kVsOnlySupportedChar, std::set<uint32_t>({kVS3, kVS19, kVS20}));
382 
383     const uint32_t kNotSupportedChar = 0x845C;
384     EXPECT_FALSE(family->getCoverage()->get(kNotSupportedChar));
385     expectVSGlyphs(family.get(), kNotSupportedChar, std::set<uint32_t>());
386 }
387 
TEST_F(FontFamilyTest,hasVSTableTest)388 TEST_F(FontFamilyTest, hasVSTableTest) {
389     struct TestCase {
390         const std::string fontPath;
391         bool hasVSTable;
392     } testCases[] = {
393         { kTestFontDir "Ja.ttf", true },
394         { kTestFontDir "ZhHant.ttf", true },
395         { kTestFontDir "ZhHans.ttf", true },
396         { kTestFontDir "Italic.ttf", false },
397         { kTestFontDir "Bold.ttf", false },
398         { kTestFontDir "BoldItalic.ttf", false },
399     };
400 
401     for (auto testCase : testCases) {
402         SCOPED_TRACE(testCase.hasVSTable ?
403                 "Font " + testCase.fontPath + " should have a variation sequence table." :
404                 "Font " + testCase.fontPath + " shouldn't have a variation sequence table.");
405 
406         MinikinAutoUnref<MinikinFontForTest> minikinFont(new MinikinFontForTest(testCase.fontPath));
407         MinikinAutoUnref<FontFamily> family(new FontFamily);
408         family->addFont(minikinFont.get());
409         AutoMutex _l(gMinikinLock);
410         family->getCoverage();
411 
412         EXPECT_EQ(testCase.hasVSTable, family->hasVSTable());
413     }
414 }
415 
416 }  // namespace android
417