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