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