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 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 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 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 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 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 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 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