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
28namespace android {
29
30typedef ICUTestBase FontLanguagesTest;
31typedef ICUTestBase FontLanguageTest;
32
33static 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
39static 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
45TEST_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
77TEST_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
104TEST_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
114TEST_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
215TEST_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
231TEST_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
248TEST_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
270TEST_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
283TEST_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)
319const char kVsTestFont[] = kTestFontDir "VarioationSelectorTest-Regular.ttf";
320
321class FontFamilyTest : public ICUTestBase {
322public:
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.
335void 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
352TEST_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
388TEST_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