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/FontCollection.h"
20 #include "minikin/LayoutPieces.h"
21
22 #include "FontTestUtils.h"
23 #include "LayoutSplitter.h"
24 #include "UnicodeUtils.h"
25
26 namespace minikin {
27 namespace {
28
parseTestString(const std::string & text)29 std::pair<std::vector<uint16_t>, Range> parseTestString(const std::string& text) {
30 auto utf16 = utf8ToUtf16(text);
31 Range range;
32 std::vector<uint16_t> outText;
33 for (uint16_t c : utf16) {
34 switch (c) {
35 case '(':
36 range.setStart(outText.size());
37 break;
38 case ')':
39 range.setEnd(outText.size());
40 break;
41 default:
42 outText.push_back(c);
43 }
44 }
45 return std::make_pair(outText, range);
46 }
47
parseExpectString(const std::string & text)48 std::pair<Range, Range> parseExpectString(const std::string& text) {
49 auto utf16 = utf8ToUtf16(text);
50 Range context;
51 Range piece;
52 uint32_t textPos = 0;
53 for (uint16_t c : utf16) {
54 switch (c) {
55 case '[':
56 context.setStart(textPos);
57 break;
58 case ']':
59 context.setEnd(textPos);
60 break;
61 case '(':
62 piece.setStart(textPos);
63 break;
64 case ')':
65 piece.setEnd(textPos);
66 break;
67 default:
68 textPos++;
69 }
70 }
71 return std::make_pair(context, piece);
72 }
73
buildDebugString(const U16StringPiece & textBuf,const Range & context,const Range & piece)74 std::string buildDebugString(const U16StringPiece& textBuf, const Range& context,
75 const Range& piece) {
76 std::vector<uint16_t> out;
77 out.reserve(textBuf.size() + 4);
78 for (uint32_t i = 0; i < textBuf.size() + 1; ++i) {
79 if (i == context.getStart()) {
80 out.push_back('[');
81 }
82 if (i == piece.getStart()) {
83 out.push_back('(');
84 }
85 if (i == piece.getEnd()) {
86 out.push_back(')');
87 }
88 if (i == context.getEnd()) {
89 out.push_back(']');
90 }
91 if (i != textBuf.size()) {
92 out.push_back(textBuf[i]);
93 }
94 }
95 return utf16ToUtf8(out);
96 }
97
TEST(LayoutSplitterTest,LTR_Latin)98 TEST(LayoutSplitterTest, LTR_Latin) {
99 struct TestCase {
100 std::string testStr;
101 std::vector<std::string> expects;
102 } testCases[] = {
103 {"(This is an example text.)",
104 {
105 "[(This)] is an example text.", "This[( )]is an example text.",
106 "This [(is)] an example text.", "This is[( )]an example text.",
107 "This is [(an)] example text.", "This is an[( )]example text.",
108 "This is an [(example)] text.", "This is an example[( )]text.",
109 "This is an example [(text.)]",
110 }},
111 {"This( is an example )text.",
112 {
113 "This[( )]is an example text.", "This [(is)] an example text.",
114 "This is[( )]an example text.", "This is [(an)] example text.",
115 "This is an[( )]example text.", "This is an [(example)] text.",
116 "This is an example[( )]text.",
117 }},
118 {"This (is an example) text.",
119 {
120 "This [(is)] an example text.", "This is[( )]an example text.",
121 "This is [(an)] example text.", "This is an[( )]example text.",
122 "This is an [(example)] text.",
123 }},
124 {"Th(is is an example te)xt.",
125 {
126 "[Th(is)] is an example text.", "This[( )]is an example text.",
127 "This [(is)] an example text.", "This is[( )]an example text.",
128 "This is [(an)] example text.", "This is an[( )]example text.",
129 "This is an [(example)] text.", "This is an example[( )]text.",
130 "This is an example [(te)xt.]",
131 }},
132 {"This is an ex(amp)le text.",
133 {
134 "This is an [ex(amp)le] text.",
135 }},
136 {"There are (three spaces.)",
137 {
138 "There are [(three)] spaces.", "There are three[( )] spaces.",
139 "There are three [( )] spaces.", "There are three [( )]spaces.",
140 "There are three [(spaces.)]",
141 }},
142 };
143
144 for (const auto& testCase : testCases) {
145 auto[text, range] = parseTestString(testCase.testStr);
146 uint32_t expectationIndex = 0;
147 for (auto[acContext, acPiece] : LayoutSplitter(text, range, false /* isRtl */)) {
148 ASSERT_NE(expectationIndex, testCase.expects.size());
149 const std::string expectString = testCase.expects[expectationIndex++];
150 auto[exContext, exPiece] = parseExpectString(expectString);
151 EXPECT_EQ(acContext, exContext)
152 << expectString << " vs " << buildDebugString(text, acContext, acPiece);
153 EXPECT_EQ(acPiece, exPiece)
154 << expectString << " vs " << buildDebugString(text, acContext, acPiece);
155 }
156 EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains";
157 }
158 }
159
TEST(LayoutSplitterTest,RTL_Latin)160 TEST(LayoutSplitterTest, RTL_Latin) {
161 struct TestCase {
162 std::string testStr;
163 std::vector<std::string> expects;
164 } testCases[] = {
165 {"(This is an example text.)",
166 {
167 "This is an example [(text.)]", "This is an example[( )]text.",
168 "This is an [(example)] text.", "This is an[( )]example text.",
169 "This is [(an)] example text.", "This is[( )]an example text.",
170 "This [(is)] an example text.", "This[( )]is an example text.",
171 "[(This)] is an example text.",
172 }},
173 {"This( is an example )text.",
174 {
175 "This is an example[( )]text.", "This is an [(example)] text.",
176 "This is an[( )]example text.", "This is [(an)] example text.",
177 "This is[( )]an example text.", "This [(is)] an example text.",
178 "This[( )]is an example text.",
179 }},
180 {"This (is an example) text.",
181 {
182 "This is an [(example)] text.", "This is an[( )]example text.",
183 "This is [(an)] example text.", "This is[( )]an example text.",
184 "This [(is)] an example text.",
185 }},
186 {"Th(is is an example te)xt.",
187 {
188 "This is an example [(te)xt.]", "This is an example[( )]text.",
189 "This is an [(example)] text.", "This is an[( )]example text.",
190 "This is [(an)] example text.", "This is[( )]an example text.",
191 "This [(is)] an example text.", "This[( )]is an example text.",
192 "[Th(is)] is an example text.",
193 }},
194 {"This is an ex(amp)le text.",
195 {
196 "This is an [ex(amp)le] text.",
197 }},
198 {"There are (three spaces.)",
199 {
200 "There are three [(spaces.)]", "There are three [( )]spaces.",
201 "There are three [( )] spaces.", "There are three[( )] spaces.",
202 "There are [(three)] spaces.",
203 }},
204 };
205
206 for (const auto& testCase : testCases) {
207 auto[text, range] = parseTestString(testCase.testStr);
208 uint32_t expectationIndex = 0;
209 for (auto[acContext, acPiece] : LayoutSplitter(text, range, true /* isRtl */)) {
210 ASSERT_NE(expectationIndex, testCase.expects.size());
211 const std::string expectString = testCase.expects[expectationIndex++];
212 auto[exContext, exPiece] = parseExpectString(expectString);
213 EXPECT_EQ(acContext, exContext)
214 << expectString << " vs " << buildDebugString(text, acContext, acPiece);
215 EXPECT_EQ(acPiece, exPiece)
216 << expectString << " vs " << buildDebugString(text, acContext, acPiece);
217 }
218 EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains";
219 }
220 }
221
TEST(LayoutSplitterTest,LTR_CJK)222 TEST(LayoutSplitterTest, LTR_CJK) {
223 struct TestCase {
224 std::string testStr;
225 std::vector<std::string> expects;
226 } testCases[] = {
227 {// All Kanji text
228 "(\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776)",
229 {
230 "[(\u6614)]\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776",
231 "\u6614[(\u8005)]\u8358\u5468\u5922\u70BA\u80E1\u8776",
232 "\u6614\u8005[(\u8358)]\u5468\u5922\u70BA\u80E1\u8776",
233 "\u6614\u8005\u8358[(\u5468)]\u5922\u70BA\u80E1\u8776",
234 "\u6614\u8005\u8358\u5468[(\u5922)]\u70BA\u80E1\u8776",
235 "\u6614\u8005\u8358\u5468\u5922[(\u70BA)]\u80E1\u8776",
236 "\u6614\u8005\u8358\u5468\u5922\u70BA[(\u80E1)]\u8776",
237 "\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1[(\u8776)]",
238 }},
239 {// Japanese text like as follows
240 // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana]
241 "(\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002)",
242 {
243 "[(\u672C)]\u65E5\u306F\u6674\u5929\u306A\u308A\u3002",
244 "\u672C[(\u65E5\u306F)]\u6674\u5929\u306A\u308A\u3002",
245 "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002",
246 "\u672C\u65E5\u306F\u6674[(\u5929\u306A\u308A\u3002)]",
247 }},
248 {// Japanese text like as follows
249 // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana]
250 "\u672C\u65E5(\u306F\u6674\u5929\u306A)\u308A\u3002",
251 {
252 "\u672C[\u65E5(\u306F)]\u6674\u5929\u306A\u308A\u3002",
253 "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002",
254 "\u672C\u65E5\u306F\u6674[(\u5929\u306A)\u308A\u3002]",
255 }},
256 };
257
258 for (const auto& testCase : testCases) {
259 auto[text, range] = parseTestString(testCase.testStr);
260 uint32_t expectationIndex = 0;
261 for (auto[acContext, acPiece] : LayoutSplitter(text, range, false /* isRtl */)) {
262 ASSERT_NE(expectationIndex, testCase.expects.size());
263 const std::string expectString = testCase.expects[expectationIndex++];
264 auto[exContext, exPiece] = parseExpectString(expectString);
265 EXPECT_EQ(acContext, exContext)
266 << expectString << " vs " << buildDebugString(text, acContext, acPiece);
267 EXPECT_EQ(acPiece, exPiece)
268 << expectString << " vs " << buildDebugString(text, acContext, acPiece);
269 }
270 EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains";
271 }
272 }
273
TEST(LayoutSplitterTest,RTL_CJK)274 TEST(LayoutSplitterTest, RTL_CJK) {
275 struct TestCase {
276 std::string testStr;
277 std::vector<std::string> expects;
278 } testCases[] = {
279 {// All Kanji text
280 "(\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776)",
281 {
282 "\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1[(\u8776)]",
283 "\u6614\u8005\u8358\u5468\u5922\u70BA[(\u80E1)]\u8776",
284 "\u6614\u8005\u8358\u5468\u5922[(\u70BA)]\u80E1\u8776",
285 "\u6614\u8005\u8358\u5468[(\u5922)]\u70BA\u80E1\u8776",
286 "\u6614\u8005\u8358[(\u5468)]\u5922\u70BA\u80E1\u8776",
287 "\u6614\u8005[(\u8358)]\u5468\u5922\u70BA\u80E1\u8776",
288 "\u6614[(\u8005)]\u8358\u5468\u5922\u70BA\u80E1\u8776",
289 "[(\u6614)]\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776",
290 }},
291 {// Japanese text like as follows
292 // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana]
293 "(\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002)",
294 {
295 "\u672C\u65E5\u306F\u6674[(\u5929\u306A\u308A\u3002)]",
296 "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002",
297 "\u672C[(\u65E5\u306F)]\u6674\u5929\u306A\u308A\u3002",
298 "[(\u672C)]\u65E5\u306F\u6674\u5929\u306A\u308A\u3002",
299 }},
300 {// Japanese text like as follows
301 // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana]
302 "\u672C\u65E5(\u306F\u6674\u5929\u306A)\u308A\u3002",
303 {
304 "\u672C\u65E5\u306F\u6674[(\u5929\u306A)\u308A\u3002]",
305 "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002",
306 "\u672C[\u65E5(\u306F)]\u6674\u5929\u306A\u308A\u3002",
307 }},
308 };
309
310 for (const auto& testCase : testCases) {
311 auto[text, range] = parseTestString(testCase.testStr);
312 uint32_t expectationIndex = 0;
313 for (auto[acContext, acPiece] : LayoutSplitter(text, range, true /* isRtl */)) {
314 ASSERT_NE(expectationIndex, testCase.expects.size());
315 const std::string expectString = testCase.expects[expectationIndex++];
316 auto[exContext, exPiece] = parseExpectString(expectString);
317 EXPECT_EQ(acContext, exContext)
318 << expectString << " vs " << buildDebugString(text, acContext, acPiece);
319 EXPECT_EQ(acPiece, exPiece)
320 << expectString << " vs " << buildDebugString(text, acContext, acPiece);
321 }
322 EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains";
323 }
324 }
325
326 } // namespace
327 } // namespace minikin
328