• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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