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 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 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 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 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 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 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 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 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 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 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 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: 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. 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 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 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