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