1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <gtest/gtest.h>
18 
19 #include "minikin/BoundsCache.h"
20 
21 #include "FontTestUtils.h"
22 #include "LocaleListCache.h"
23 #include "UnicodeUtils.h"
24 
25 namespace minikin {
26 
27 class TestableBoundsCache : public BoundsCache {
28 public:
TestableBoundsCache(uint32_t maxEntries)29     TestableBoundsCache(uint32_t maxEntries) : BoundsCache(maxEntries) {}
30 };
31 
32 class BoundsCapture {
33 public:
BoundsCapture()34     BoundsCapture() {}
35 
operator ()(const MinikinRect & rect,float advance)36     void operator()(const MinikinRect& rect, float advance) {
37         mRect = rect;
38         mAdvance = advance;
39     }
40 
rect() const41     const MinikinRect& rect() const { return mRect; }
advance() const42     float advance() const { return mAdvance; }
43 
44 private:
45     MinikinRect mRect;
46     float mAdvance;
47 };
48 
TEST(BoundsCacheTest,cacheHitTest)49 TEST(BoundsCacheTest, cacheHitTest) {
50     auto text = utf8ToUtf16("android");
51     Range range(0, text.size());
52     MinikinPaint paint(buildFontCollection("Ascii.ttf"));
53 
54     TestableBoundsCache boundsCache(10);
55 
56     BoundsCapture bounds1;
57     boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
58                             EndHyphenEdit::NO_EDIT, bounds1);
59 
60     BoundsCapture bounds2;
61     boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
62                             EndHyphenEdit::NO_EDIT, bounds2);
63 
64     EXPECT_EQ(bounds1.rect(), bounds2.rect());
65     EXPECT_EQ(bounds1.advance(), bounds2.advance());
66 }
67 
TEST(BoundsCacheTest,cacheMissTest)68 TEST(BoundsCacheTest, cacheMissTest) {
69     auto text1 = utf8ToUtf16("android");
70     auto text2 = utf8ToUtf16("αβγδζ");
71     MinikinPaint paint(buildFontCollection("Ascii.ttf"));
72 
73     TestableBoundsCache boundsCache(10);
74 
75     BoundsCapture bounds1;
76     BoundsCapture bounds2;
77 
78     {
79         SCOPED_TRACE("Different text");
80         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
81                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
82         boundsCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */,
83                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
84         EXPECT_NE(bounds1.rect(), bounds2.rect());
85         EXPECT_NE(bounds1.advance(), bounds2.advance());
86     }
87     {
88         SCOPED_TRACE("Different range");
89         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
90                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
91         boundsCache.getOrCreate(text1, Range(1, text1.size()), paint, false /* LTR */,
92                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
93         EXPECT_NE(bounds1.rect(), bounds2.rect());
94         EXPECT_NE(bounds1.advance(), bounds2.advance());
95     }
96     {
97         SCOPED_TRACE("Different collection");
98         MinikinPaint paint1(buildFontCollection("Ascii.ttf"));
99         paint1.size = 10.0f;
100         paint1.scaleX = 1.0f;
101         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
102                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
103         MinikinPaint paint2(buildFontCollection("Emoji.ttf"));
104         paint2.size = 10.0f;
105         paint2.scaleX = 1.0f;
106         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
107                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
108         EXPECT_NE(bounds1.rect(), bounds2.rect());
109         EXPECT_NE(bounds1.advance(), bounds2.advance());
110     }
111     {
112         SCOPED_TRACE("Different size");
113         auto collection = buildFontCollection("Ascii.ttf");
114         MinikinPaint paint1(collection);
115         paint1.size = 10.0f;
116         paint1.scaleX = 1.0f;
117         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
118                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
119         MinikinPaint paint2(collection);
120         paint2.size = 20.0f;
121         paint2.scaleX = 1.0f;
122         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
123                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
124         EXPECT_NE(bounds1.rect(), bounds2.rect());
125         EXPECT_NE(bounds1.advance(), bounds2.advance());
126     }
127     {
128         SCOPED_TRACE("Different letter spacing");
129         auto collection = buildFontCollection("Ascii.ttf");
130         MinikinPaint paint1(collection);
131         paint1.letterSpacing = 0.0f;
132         paint1.size = 10.0f;
133         paint1.scaleX = 1.0f;
134         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
135                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1);
136         MinikinPaint paint2(collection);
137         paint2.letterSpacing = 1.0f;
138         paint2.size = 10.0f;
139         paint2.scaleX = 1.0f;
140         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
141                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
142         EXPECT_NE(bounds1.rect(), bounds2.rect());
143         EXPECT_NE(bounds1.advance(), bounds2.advance());
144     }
145 }
146 
TEST(BoundsCacheTest,cacheOverflowTest)147 TEST(BoundsCacheTest, cacheOverflowTest) {
148     auto text = utf8ToUtf16("android");
149     Range range(0, text.size());
150     MinikinPaint paint(buildFontCollection("Ascii.ttf"));
151 
152     TestableBoundsCache boundsCache(5);
153 
154     BoundsCapture bounds1;
155     boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
156                             EndHyphenEdit::NO_EDIT, bounds1);
157 
158     for (char c = 'a'; c <= 'z'; c++) {
159         auto text1 = utf8ToUtf16(std::string(10, c));
160         BoundsCapture bounds2;
161         boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
162                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2);
163     }
164 
165     BoundsCapture bounds3;
166     boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
167                             EndHyphenEdit::NO_EDIT, bounds3);
168     EXPECT_EQ(bounds1.rect(), bounds3.rect());
169     EXPECT_EQ(bounds1.advance(), bounds3.advance());
170 }
171 
172 }  // namespace minikin
173