• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 <memory>
18 
19 #include <gtest/gtest.h>
20 
21 #include "minikin/Hyphenator.h"
22 
23 #include "FileUtils.h"
24 #include "FontTestUtils.h"
25 #include "GreedyLineBreaker.h"
26 #include "HyphenatorMap.h"
27 #include "LineBreakerTestHelper.h"
28 #include "LocaleListCache.h"
29 #include "MinikinInternal.h"
30 #include "UnicodeUtils.h"
31 #include "WordBreaker.h"
32 
33 namespace minikin {
34 namespace {
35 
36 using line_breaker_test_helper::ConstantRun;
37 using line_breaker_test_helper::LineBreakExpectation;
38 using line_breaker_test_helper::RectangleLineWidth;
39 using line_breaker_test_helper::sameLineBreak;
40 using line_breaker_test_helper::toString;
41 
42 // The ascent/descent of Ascii.ttf with text size = 10.
43 constexpr float ASCENT = -80.0f;
44 constexpr float DESCENT = 20.0f;
45 
46 // The ascent/descent of CustomExtent.ttf with text size = 10.
47 constexpr float CUSTOM_ASCENT = -160.0f;
48 constexpr float CUSTOM_DESCENT = 40.0f;
49 
50 class GreedyLineBreakerTest : public testing::Test {
51 public:
GreedyLineBreakerTest()52     GreedyLineBreakerTest() {}
53 
~GreedyLineBreakerTest()54     virtual ~GreedyLineBreakerTest() {}
55 
SetUp()56     virtual void SetUp() override {
57         mHyphenationPattern = readWholeFile("/system/usr/hyphen-data/hyph-en-us.hyb");
58         Hyphenator* hyphenator = Hyphenator::loadBinary(
59                 mHyphenationPattern.data(), 2 /* min prefix */, 2 /* min suffix */, "en-US");
60         HyphenatorMap::add("en-US", hyphenator);
61         HyphenatorMap::add("pl", Hyphenator::loadBinary(nullptr, 0, 0, "pl"));
62     }
63 
TearDown()64     virtual void TearDown() override { HyphenatorMap::clear(); }
65 
66 protected:
doLineBreak(const U16StringPiece & textBuffer,bool doHyphenation,float lineWidth)67     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, bool doHyphenation,
68                                 float lineWidth) {
69         return doLineBreak(textBuffer, doHyphenation, "en-US", lineWidth);
70     }
71 
doLineBreak(const U16StringPiece & textBuffer,bool doHyphenation,const std::string & lang,float lineWidth)72     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, bool doHyphenation,
73                                 const std::string& lang, float lineWidth) {
74         MeasuredTextBuilder builder;
75         auto family1 = buildFontFamily("Ascii.ttf");
76         auto family2 = buildFontFamily("CustomExtent.ttf");
77         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
78         auto fc = std::make_shared<FontCollection>(families);
79         MinikinPaint paint(fc);
80         paint.size = 10.0f;  // Make 1em=10px
81         paint.localeListId = LocaleListCache::getId(lang);
82         builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
83         std::unique_ptr<MeasuredText> measuredText =
84                 builder.build(textBuffer, false /* compute hyphenation */,
85                               false /* compute full layout */, nullptr /* no hint */);
86         RectangleLineWidth rectangleLineWidth(lineWidth);
87         TabStops tabStops(nullptr, 0, 10);
88         return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
89                                doHyphenation);
90     }
91 
92 private:
93     std::vector<uint8_t> mHyphenationPattern;
94 };
95 
TEST_F(GreedyLineBreakerTest,testBreakWithoutHyphenation)96 TEST_F(GreedyLineBreakerTest, testBreakWithoutHyphenation) {
97     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
98     const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
99 
100     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
101     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
102     // Note that disable clang-format everywhere since aligned expectation is more readable.
103     {
104         constexpr float LINE_WIDTH = 1000;
105         std::vector<LineBreakExpectation> expect = {
106                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
107         };
108 
109         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
110         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
111                                                    << " vs " << std::endl
112                                                    << toString(textBuf, actual);
113     }
114     {
115         constexpr float LINE_WIDTH = 240;
116         std::vector<LineBreakExpectation> expect = {
117                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
118         };
119 
120         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
121         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
122                                                    << " vs " << std::endl
123                                                    << toString(textBuf, actual);
124     }
125     {
126         constexpr float LINE_WIDTH = 230;
127         // clang-format off
128         std::vector<LineBreakExpectation> expect = {
129                 { "This is an example ", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
130                 { "text."              ,  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
131         };
132         // clang-format on
133 
134         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
135         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
136                                                    << " vs " << std::endl
137                                                    << toString(textBuf, actual);
138     }
139     {
140         constexpr float LINE_WIDTH = 80;
141         // clang-format off
142         std::vector<LineBreakExpectation> expect = {
143                 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
144                 { "an "     , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
145                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
146                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
147         };
148         // clang-format on
149 
150         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
151         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
152                                                    << " vs " << std::endl
153                                                    << toString(textBuf, actual);
154     }
155     {
156         constexpr float LINE_WIDTH = 70;
157         // clang-format off
158         std::vector<LineBreakExpectation> expect = {
159                 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
160                 { "an "     , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
161                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
162                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
163         };
164         // clang-format on
165 
166         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
167         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
168                                                    << " vs " << std::endl
169                                                    << toString(textBuf, actual);
170     }
171     {
172         constexpr float LINE_WIDTH = 60;
173         // clang-format off
174         std::vector<LineBreakExpectation> expect = {
175                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
176                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
177                 { "exampl", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
178                 { "e "    , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
179                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
180         };
181         // clang-format on
182 
183         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
184         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
185                                                    << " vs " << std::endl
186                                                    << toString(textBuf, actual);
187     }
188     {
189         constexpr float LINE_WIDTH = 50;
190         // clang-format off
191         std::vector<LineBreakExpectation> expect = {
192                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
193                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
194                 { "examp" , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
195                 { "le "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
196                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
197         };
198         // clang-format on
199 
200         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
201         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
202                                                    << " vs " << std::endl
203                                                    << toString(textBuf, actual);
204     }
205     {
206         constexpr float LINE_WIDTH = 40;
207         // clang-format off
208         std::vector<LineBreakExpectation> expect = {
209                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
210                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
211                 { "an "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
212                 { "exam"  , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
213                 { "ple "  , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
214                 { "text"  , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
215                 { "."     , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
216         };
217         // clang-format on
218 
219         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
220         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
221                                                    << " vs " << std::endl
222                                                    << toString(textBuf, actual);
223     }
224     {
225         constexpr float LINE_WIDTH = 30;
226         // clang-format off
227         std::vector<LineBreakExpectation> expect = {
228                 { "Thi" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
229                 { "s "  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
230                 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
231                 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
232                 { "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
233                 { "mpl" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
234                 { "e "  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
235                 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
236                 { "t."  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
237         };
238         // clang-format on
239 
240         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
241         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
242                                                    << " vs " << std::endl
243                                                    << toString(textBuf, actual);
244     }
245     {
246         constexpr float LINE_WIDTH = 20;
247         // clang-format off
248         std::vector<LineBreakExpectation> expect = {
249                 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
250                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
251                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
252                 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
253                 { "ex" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
254                 { "am" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
255                 { "pl" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
256                 { "e " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
257                 { "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
258                 { "xt" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
259                 { "."  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
260         };
261         // clang-format on
262 
263         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
264         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
265                                                    << " vs " << std::endl
266                                                    << toString(textBuf, actual);
267     }
268     {
269         constexpr float LINE_WIDTH = 10;
270         // clang-format off
271         std::vector<LineBreakExpectation> expect = {
272                 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
273                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
274                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
275                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
276                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
277                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
278                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
279                 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
280                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
281                 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
282                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
283                 { "m" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
284                 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
285                 { "l" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
286                 { "e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
287                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
288                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
289                 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
290                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
291                 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
292         };
293         // clang-format on
294 
295         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
296         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
297                                                    << " vs " << std::endl
298                                                    << toString(textBuf, actual);
299     }
300 }
301 
TEST_F(GreedyLineBreakerTest,testBreakWithHyphenation)302 TEST_F(GreedyLineBreakerTest, testBreakWithHyphenation) {
303     constexpr bool NO_HYPHEN = true;  // Do hyphenation in this test case.
304     // "hyphenation" is hyphnated to "hy-phen-a-tion".
305     const std::vector<uint16_t> textBuf = utf8ToUtf16("Hyphenation is hyphenation.");
306 
307     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
308     constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
309     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
310 
311     // Note that disable clang-format everywhere since aligned expectation is more readable.
312     {
313         constexpr float LINE_WIDTH = 1000;
314         std::vector<LineBreakExpectation> expect = {
315                 {"Hyphenation is hyphenation.", 270, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT,
316                  DESCENT},
317         };
318 
319         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
320         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
321                                                    << " vs " << std::endl
322                                                    << toString(textBuf, actual);
323     }
324     {
325         constexpr float LINE_WIDTH = 270;
326         std::vector<LineBreakExpectation> expect = {
327                 {"Hyphenation is hyphenation.", 270, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT,
328                  DESCENT},
329         };
330 
331         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
332         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
333                                                    << " vs " << std::endl
334                                                    << toString(textBuf, actual);
335     }
336     {
337         constexpr float LINE_WIDTH = 260;
338         // clang-format off
339         std::vector<LineBreakExpectation> expect = {
340                 { "Hyphenation is " , 140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
341                 { "hyphenation."    , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
342         };
343         // clang-format on
344 
345         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
346         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
347                                                    << " vs " << std::endl
348                                                    << toString(textBuf, actual);
349     }
350     {
351         constexpr float LINE_WIDTH = 170;
352         // clang-format off
353         std::vector<LineBreakExpectation> expect = {
354                 { "Hyphenation is " , 140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
355                 { "hyphenation."    , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
356         };
357         // clang-format on
358 
359         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
360         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
361                                                    << " vs " << std::endl
362                                                    << toString(textBuf, actual);
363     }
364     {
365         constexpr float LINE_WIDTH = 120;
366         // clang-format off
367         std::vector<LineBreakExpectation> expect = {
368                 { "Hyphenation " , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
369                 { "is "          , 20 , NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
370                 { "hyphenation." , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
371         };
372         // clang-format on
373 
374         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
375         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
376                                                    << " vs " << std::endl
377                                                    << toString(textBuf, actual);
378     }
379     {
380         constexpr float LINE_WIDTH = 100;
381         // clang-format off
382         std::vector<LineBreakExpectation> expect = {
383                 { "Hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
384                 { "tion is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
385                 { "hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
386                 { "tion."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
387         };
388         // clang-format on
389 
390         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
391         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
392                                                    << " vs " << std::endl
393                                                    << toString(textBuf, actual);
394     }
395     {
396         constexpr float LINE_WIDTH = 80;
397         // clang-format off
398         std::vector<LineBreakExpectation> expect = {
399                 { "Hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
400                 { "tion is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
401                 { "hyphena-", 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
402                 { "tion."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
403         };
404         // clang-format on
405 
406         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
407         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
408                                                    << " vs " << std::endl
409                                                    << toString(textBuf, actual);
410     }
411     {
412         constexpr float LINE_WIDTH = 70;
413         // clang-format off
414         std::vector<LineBreakExpectation> expect = {
415                 { "Hyphen-", 70, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
416                 { "ation " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
417                 { "is "    , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
418                 { "hyphen-", 70, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
419                 { "ation." , 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
420         };
421         // clang-format on
422 
423         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
424         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
425                                                    << " vs " << std::endl
426                                                    << toString(textBuf, actual);
427     }
428     {
429         constexpr float LINE_WIDTH = 60;
430         // clang-format off
431         std::vector<LineBreakExpectation> expect = {
432                 { "Hy-"   , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
433                 { "phena-", 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
434                 { "tion " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
435                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
436                 { "hy-"   , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
437                 { "phena-", 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
438                 { "tion." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
439         };
440         // clang-format on
441 
442         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
443         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
444                                                    << " vs " << std::endl
445                                                    << toString(textBuf, actual);
446     }
447     {
448         constexpr float LINE_WIDTH = 50;
449         // clang-format off
450         std::vector<LineBreakExpectation> expect = {
451                 { "Hy-"   , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
452                 { "phen-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
453                 { "ation ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
454                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
455                 { "hy-"   , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
456                 { "phen-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
457                 { "a-"    , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
458                 { "tion." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
459         };
460         // clang-format on
461 
462         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
463         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
464                                                    << " vs " << std::endl
465                                                    << toString(textBuf, actual);
466     }
467     {
468         constexpr float LINE_WIDTH = 40;
469         // clang-format off
470         std::vector<LineBreakExpectation> expect = {
471                 { "Hy-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
472                 { "phen" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
473                 { "a-"   , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
474                 { "tion ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
475                 { "is "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
476                 { "hy-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
477                 { "phen" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
478                 { "a-"   , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
479                 { "tion" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
480                 { "."    , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
481         };
482         // clang-format on
483 
484         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
485         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
486                                                    << " vs " << std::endl
487                                                    << toString(textBuf, actual);
488     }
489     {
490         constexpr float LINE_WIDTH = 30;
491         // clang-format off
492         std::vector<LineBreakExpectation> expect = {
493                 { "Hy-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
494                 { "phe", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
495                 { "na-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
496                 { "tio", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
497                 { "n " , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
498                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
499                 { "hy-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
500                 { "phe", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
501                 { "na-", 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
502                 { "tio", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
503                 { "n." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
504         };
505         // clang-format on
506 
507         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
508         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
509                                                    << " vs " << std::endl
510                                                    << toString(textBuf, actual);
511     }
512     {
513         constexpr float LINE_WIDTH = 20;
514         // clang-format off
515         std::vector<LineBreakExpectation> expect = {
516                 { "Hy" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
517                 { "ph" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
518                 { "en" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
519                 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
520                 { "ti" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
521                 { "on ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
522                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
523                 { "hy" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
524                 { "ph" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
525                 { "en" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
526                 { "a-" , 20, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
527                 { "ti" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
528                 { "on" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
529                 { "."  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
530         };
531         // clang-format on
532 
533         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
534         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
535                                                    << " vs " << std::endl
536                                                    << toString(textBuf, actual);
537     }
538     {
539         constexpr float LINE_WIDTH = 10;
540         // clang-format off
541         std::vector<LineBreakExpectation> expect = {
542                 { "H" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
543                 { "y" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
544                 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
545                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
546                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
547                 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
548                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
549                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
550                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
551                 { "o" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
552                 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
553                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
554                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
555                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
556                 { "y" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
557                 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
558                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
559                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
560                 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
561                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
562                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
563                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
564                 { "o" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
565                 { "n" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
566                 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
567         };
568         // clang-format on
569 
570         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
571         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
572                                                    << " vs " << std::endl
573                                                    << toString(textBuf, actual);
574     }
575 }
576 
TEST_F(GreedyLineBreakerTest,testHyphenationStartLineChange)577 TEST_F(GreedyLineBreakerTest, testHyphenationStartLineChange) {
578     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
579     // "hyphenation" is hyphnated to "hy-phen-a-tion".
580     const std::vector<uint16_t> textBuf = utf8ToUtf16("czerwono-niebieska");
581 
582     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
583     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
584     constexpr StartHyphenEdit START_HYPHEN = StartHyphenEdit::INSERT_HYPHEN;
585 
586     // Note that disable clang-format everywhere since aligned expectation is more readable.
587     {
588         constexpr float LINE_WIDTH = 1000;
589         std::vector<LineBreakExpectation> expect = {
590                 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
591         };
592 
593         const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
594         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
595                                                    << " vs " << std::endl
596                                                    << toString(textBuf, actual);
597     }
598     {
599         constexpr float LINE_WIDTH = 180;
600         std::vector<LineBreakExpectation> expect = {
601                 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
602         };
603 
604         const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
605         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
606                                                    << " vs " << std::endl
607                                                    << toString(textBuf, actual);
608     }
609     {
610         constexpr float LINE_WIDTH = 130;
611         // clang-format off
612         std::vector<LineBreakExpectation> expect = {
613                 {"czerwono-" ,  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
614                 {"-niebieska", 100,    START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
615         };
616         // clang-format on
617 
618         const auto actual = doLineBreak(textBuf, DO_HYPHEN, "pl", LINE_WIDTH);
619         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
620                                                    << " vs " << std::endl
621                                                    << toString(textBuf, actual);
622     }
623 }
624 
TEST_F(GreedyLineBreakerTest,testZeroWidthLine)625 TEST_F(GreedyLineBreakerTest, testZeroWidthLine) {
626     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
627     constexpr float LINE_WIDTH = 0;
628 
629     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
630     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
631 
632     {
633         const auto textBuf = utf8ToUtf16("");
634         std::vector<LineBreakExpectation> expect = {};
635         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
636         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
637                                                    << " vs " << std::endl
638                                                    << toString(textBuf, actual);
639     }
640     {
641         const auto textBuf = utf8ToUtf16("A");
642         std::vector<LineBreakExpectation> expect = {
643                 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
644         };
645         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
646         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
647                                                    << " vs " << std::endl
648                                                    << toString(textBuf, actual);
649     }
650     {
651         const auto textBuf = utf8ToUtf16("AB");
652         std::vector<LineBreakExpectation> expect = {
653                 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
654                 {"B", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
655         };
656         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
657         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
658                                                    << " vs " << std::endl
659                                                    << toString(textBuf, actual);
660     }
661 }
662 
TEST_F(GreedyLineBreakerTest,testZeroWidthCharacter)663 TEST_F(GreedyLineBreakerTest, testZeroWidthCharacter) {
664     constexpr float CHAR_WIDTH = 0.0;
665     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
666 
667     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
668     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
669     {
670         constexpr float LINE_WIDTH = 1.0;
671         const auto textBuf = utf8ToUtf16("This is an example text.");
672         std::vector<LineBreakExpectation> expect = {
673                 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
674         };
675 
676         MeasuredTextBuilder builder;
677         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
678                                           DESCENT);
679         std::unique_ptr<MeasuredText> measuredText =
680                 builder.build(textBuf, false /* compute hyphenation */,
681                               false /* compute full layout */, nullptr /* no hint */);
682         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
683         TabStops tabStops(nullptr, 0, 10);
684         const auto actual =
685                 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
686         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
687                                                    << " vs " << std::endl
688                                                    << toString(textBuf, actual);
689     }
690     {
691         constexpr float LINE_WIDTH = 0.0;
692         const auto textBuf = utf8ToUtf16("This is an example text.");
693         std::vector<LineBreakExpectation> expect = {
694                 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
695         };
696         MeasuredTextBuilder builder;
697         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
698                                           DESCENT);
699         std::unique_ptr<MeasuredText> measuredText =
700                 builder.build(textBuf, false /* compute hyphenation */,
701                               false /* compute full layout */, nullptr /* no hint */);
702         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
703         TabStops tabStops(nullptr, 0, 10);
704         const auto actual =
705                 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
706         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
707                                                    << " vs " << std::endl
708                                                    << toString(textBuf, actual);
709     }
710 }
711 
TEST_F(GreedyLineBreakerTest,testLocaleSwitchTest)712 TEST_F(GreedyLineBreakerTest, testLocaleSwitchTest) {
713     constexpr float CHAR_WIDTH = 10.0;
714     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
715 
716     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
717     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
718 
719     constexpr float LINE_WIDTH = 240;
720     const auto textBuf = utf8ToUtf16("This is an example text.");
721     {
722         std::vector<LineBreakExpectation> expect = {
723                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
724         };
725 
726         MeasuredTextBuilder builder;
727         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
728         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
729                                           DESCENT);
730         std::unique_ptr<MeasuredText> measuredText =
731                 builder.build(textBuf, false /* compute hyphenation */,
732                               false /* compute full layout */, nullptr /* no hint */);
733         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
734         TabStops tabStops(nullptr, 0, 0);
735 
736         const auto actual =
737                 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
738         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
739                                                    << " vs " << std::endl
740                                                    << toString(textBuf, actual);
741     }
742     {
743         std::vector<LineBreakExpectation> expect = {
744                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
745         };
746 
747         MeasuredTextBuilder builder;
748         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
749         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
750                                           DESCENT);
751         std::unique_ptr<MeasuredText> measuredText =
752                 builder.build(textBuf, false /* compute hyphenation */,
753                               false /* compute full layout */, nullptr /* no hint */);
754         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
755         TabStops tabStops(nullptr, 0, 0);
756 
757         const auto actual =
758                 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
759         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
760                                                    << " vs " << std::endl
761                                                    << toString(textBuf, actual);
762     }
763 }
764 
TEST_F(GreedyLineBreakerTest,testEmailOrUrl)765 TEST_F(GreedyLineBreakerTest, testEmailOrUrl) {
766     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
767 
768     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
769     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
770     {
771         constexpr float LINE_WIDTH = 240;
772         const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
773         std::vector<LineBreakExpectation> expect = {
774                 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
775                 {"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
776         };
777         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
778         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
779                                                    << " vs " << std::endl
780                                                    << toString(textBuf, actual);
781     }
782     {
783         constexpr float LINE_WIDTH = 240;
784         const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
785         std::vector<LineBreakExpectation> expect = {
786                 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
787                 {"a@example.com", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
788         };
789         const auto actual = doLineBreak(textBuf, DO_HYPHEN, LINE_WIDTH);
790         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
791                                                    << " vs " << std::endl
792                                                    << toString(textBuf, actual);
793     }
794 }
795 
TEST_F(GreedyLineBreakerTest,testLocaleSwitch_InEmailOrUrl)796 TEST_F(GreedyLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
797     constexpr float CHAR_WIDTH = 10.0;
798     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
799 
800     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
801     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
802 
803     constexpr float LINE_WIDTH = 240;
804     {
805         const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
806         std::vector<LineBreakExpectation> expect = {
807                 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
808                 {"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
809         };
810 
811         MeasuredTextBuilder builder;
812         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
813         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
814                                           DESCENT);
815         std::unique_ptr<MeasuredText> measuredText =
816                 builder.build(textBuf, false /* compute hyphenation */,
817                               false /* compute full layout */, nullptr /* no hint */);
818         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
819         TabStops tabStops(nullptr, 0, 0);
820 
821         const auto actual =
822                 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
823         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
824                                                    << " vs " << std::endl
825                                                    << toString(textBuf, actual);
826     }
827     {
828         const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
829         std::vector<LineBreakExpectation> expect = {
830                 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
831                 {"a@example.com", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
832         };
833 
834         MeasuredTextBuilder builder;
835         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
836         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
837                                           DESCENT);
838         std::unique_ptr<MeasuredText> measuredText =
839                 builder.build(textBuf, false /* compute hyphenation */,
840                               false /* compute full layout */, nullptr /* no hint */);
841         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
842         TabStops tabStops(nullptr, 0, 0);
843 
844         const auto actual =
845                 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
846         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
847                                                    << " vs " << std::endl
848                                                    << toString(textBuf, actual);
849     }
850 }
851 
852 // b/68669534
TEST_F(GreedyLineBreakerTest,CrashFix_Space_Tab)853 TEST_F(GreedyLineBreakerTest, CrashFix_Space_Tab) {
854     constexpr float CHAR_WIDTH = 10.0;
855     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
856 
857     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
858     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
859     {
860         constexpr float LINE_WIDTH = 50;
861         const auto textBuf = utf8ToUtf16("a \tb");
862         std::vector<LineBreakExpectation> expect = {
863                 {"a \tb", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
864         };
865 
866         MeasuredTextBuilder builder;
867         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
868                                           DESCENT);
869         std::unique_ptr<MeasuredText> measuredText =
870                 builder.build(textBuf, false /* compute hyphenation */,
871                               false /* compute full layout */, nullptr /* no hint */);
872         RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
873         TabStops tabStops(nullptr, 0, CHAR_WIDTH);
874 
875         const auto actual =
876                 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
877         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
878                                                    << " vs " << std::endl
879                                                    << toString(textBuf, actual);
880     }
881 }
882 
TEST_F(GreedyLineBreakerTest,ExtentTest)883 TEST_F(GreedyLineBreakerTest, ExtentTest) {
884     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
885     const std::vector<uint16_t> textBuf = utf8ToUtf16("The \u3042\u3044\u3046 is Japanese.");
886 
887     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
888     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
889     {
890         constexpr float LINE_WIDTH = 1000;
891         std::vector<LineBreakExpectation> expect = {
892                 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
893                  CUSTOM_ASCENT, CUSTOM_DESCENT},
894         };
895 
896         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
897         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
898                                                    << " vs " << std::endl
899                                                    << toString(textBuf, actual);
900     }
901     {
902         constexpr float LINE_WIDTH = 200;
903         std::vector<LineBreakExpectation> expect = {
904                 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
905                  CUSTOM_ASCENT, CUSTOM_DESCENT},
906         };
907 
908         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
909         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
910                                                    << " vs " << std::endl
911                                                    << toString(textBuf, actual);
912     }
913     {
914         constexpr float LINE_WIDTH = 190;
915         std::vector<LineBreakExpectation> expect = {
916                 {"The \u3042\u3044\u3046 is ", 100, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
917                  CUSTOM_DESCENT},
918                 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
919         };
920 
921         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
922         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
923                                                    << " vs " << std::endl
924                                                    << toString(textBuf, actual);
925     }
926     {
927         constexpr float LINE_WIDTH = 90;
928         std::vector<LineBreakExpectation> expect = {
929                 {"The \u3042\u3044\u3046 ", 70, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
930                  CUSTOM_DESCENT},
931                 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
932                 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
933         };
934 
935         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
936         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
937                                                    << " vs " << std::endl
938                                                    << toString(textBuf, actual);
939     }
940     {
941         constexpr float LINE_WIDTH = 50;
942         std::vector<LineBreakExpectation> expect = {
943                 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
944                 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
945                  CUSTOM_DESCENT},
946                 {"Japan", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
947                 {"ese.", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
948         };
949 
950         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
951         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
952                                                    << " vs " << std::endl
953                                                    << toString(textBuf, actual);
954     }
955     {
956         constexpr float LINE_WIDTH = 40;
957         std::vector<LineBreakExpectation> expect = {
958                 {"The ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
959                 {"\u3042\u3044\u3046 ", 30, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
960                  CUSTOM_DESCENT},
961                 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
962                 {"Japa", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
963                 {"nese", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
964                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
965         };
966 
967         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
968         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
969                                                    << " vs " << std::endl
970                                                    << toString(textBuf, actual);
971     }
972     {
973         constexpr float LINE_WIDTH = 20;
974         std::vector<LineBreakExpectation> expect = {
975                 {"Th", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
976                 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
977                 {"\u3042\u3044", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
978                 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
979                 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
980                 {"Ja", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
981                 {"pa", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
982                 {"ne", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
983                 {"se", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
984                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
985         };
986 
987         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
988         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
989                                                    << " vs " << std::endl
990                                                    << toString(textBuf, actual);
991     }
992     {
993         constexpr float LINE_WIDTH = 10;
994         std::vector<LineBreakExpectation> expect = {
995                 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
996                 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
997                 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
998                 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
999                 {"\u3044", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1000                 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1001                 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1002                 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1003                 {"J", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1004                 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1005                 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1006                 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1007                 {"n", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1008                 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1009                 {"s", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1010                 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1011                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1012         };
1013 
1014         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1015         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1016                                                    << " vs " << std::endl
1017                                                    << toString(textBuf, actual);
1018     }
1019 }
1020 
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_SingleChar)1021 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
1022     constexpr float CHAR_WIDTH = 10.0;
1023     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1024 
1025     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1026     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1027 
1028     const auto textBuf = utf8ToUtf16("This is an example \u2639 text.");
1029 
1030     // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1031     auto doLineBreak = [=](float width) {
1032         MeasuredTextBuilder builder;
1033         builder.addCustomRun<ConstantRun>(Range(0, 19), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1034         builder.addReplacementRun(19, 20, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1035         builder.addCustomRun<ConstantRun>(Range(20, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1036                                           DESCENT);
1037 
1038         std::unique_ptr<MeasuredText> measuredText =
1039                 builder.build(textBuf, false /* compute hyphenation */,
1040                               false /* compute full layout */, nullptr /* no hint */);
1041         RectangleLineWidth rectangleLineWidth(width);
1042         TabStops tabStops(nullptr, 0, 0);
1043         return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
1044     };
1045 
1046     {
1047         constexpr float LINE_WIDTH = 100;
1048         // "is an" is a single replacement span. Do not break.
1049         // clang-format off
1050         std::vector<LineBreakExpectation> expect = {
1051                 {"This is an ",   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1052                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1053                 {"\u2639 ",        50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1054                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1055         };
1056         // clang-format on
1057         const auto actual = doLineBreak(LINE_WIDTH);
1058         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1059                                                    << " vs " << std::endl
1060                                                    << toString(textBuf, actual);
1061     }
1062     {
1063         constexpr float LINE_WIDTH = 90;
1064         // "is an" is a single replacement span. Do not break.
1065         // clang-format off
1066         std::vector<LineBreakExpectation> expect = {
1067                 {"This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1068                 {"an ",      20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1069                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1070                 {"\u2639 ",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1071                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1072         };
1073         // clang-format on
1074         const auto actual = doLineBreak(LINE_WIDTH);
1075         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1076                                                    << " vs " << std::endl
1077                                                    << toString(textBuf, actual);
1078     }
1079     {
1080         constexpr float LINE_WIDTH = 10;
1081         // "is an" is a single replacement span. Do not break.
1082         // clang-format off
1083         std::vector<LineBreakExpectation> expect = {
1084                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1085                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1086                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1087                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1088                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1089                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1090                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1091                 {"n ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1092                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1093                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1094                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1095                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1096                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1097                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1098                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1099                 // TODO: This should be "\u2639 " since should not count the trailing line end space
1100                 {"\u2639", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1101                 {" ",       0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1102                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1103                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1104                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1105                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1106                 {".",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1107         };
1108         // clang-format on
1109         const auto actual = doLineBreak(LINE_WIDTH);
1110         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1111                                                    << " vs " << std::endl
1112                                                    << toString(textBuf, actual);
1113     }
1114 }
1115 
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_MultipleChars)1116 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
1117     constexpr float CHAR_WIDTH = 10.0;
1118     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1119 
1120     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1121     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1122 
1123     const auto textBuf = utf8ToUtf16("This is an example text.");
1124 
1125     // In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
1126     auto doLineBreak = [=](float width) {
1127         MeasuredTextBuilder builder;
1128         builder.addCustomRun<ConstantRun>(Range(0, 5), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1129         builder.addReplacementRun(5, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1130         builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1131                                           DESCENT);
1132 
1133         std::unique_ptr<MeasuredText> measuredText =
1134                 builder.build(textBuf, false /* compute hyphenation */,
1135                               false /* compute full layout */, nullptr /* no hint */);
1136         RectangleLineWidth rectangleLineWidth(width);
1137         TabStops tabStops(nullptr, 0, 0);
1138         return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
1139     };
1140 
1141     {
1142         constexpr float LINE_WIDTH = 100;
1143         // "is an" is a single replacement span. Do not break.
1144         // clang-format off
1145         std::vector<LineBreakExpectation> expect = {
1146                 {"This is an ",   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1147                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1148                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1149         };
1150         // clang-format on
1151         const auto actual = doLineBreak(LINE_WIDTH);
1152         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1153                                                    << " vs " << std::endl
1154                                                    << toString(textBuf, actual);
1155     }
1156     {
1157         constexpr float LINE_WIDTH = 90;
1158         // "is an" is a single replacement span. Do not break.
1159         // clang-format off
1160         std::vector<LineBreakExpectation> expect = {
1161                 {"This ",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1162                 {"is an ",  50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1163                 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1164                 {"text.",   50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1165         };
1166         // clang-format on
1167         const auto actual = doLineBreak(LINE_WIDTH);
1168         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1169                                                    << " vs " << std::endl
1170                                                    << toString(textBuf, actual);
1171     }
1172     {
1173         constexpr float LINE_WIDTH = 10;
1174         // "is an" is a single replacement span. Do not break.
1175         // clang-format off
1176         std::vector<LineBreakExpectation> expect = {
1177                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1178                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1179                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1180                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1181                 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1182                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1183                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1184                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1185                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1186                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1187                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1188                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1189                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1190                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1191                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1192                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1193                 {".",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1194         };
1195         // clang-format on
1196         const auto actual = doLineBreak(LINE_WIDTH);
1197         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1198                                                    << " vs " << std::endl
1199                                                    << toString(textBuf, actual);
1200     }
1201 }
1202 
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_CJK)1203 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
1204     constexpr float CHAR_WIDTH = 10.0;
1205     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1206 
1207     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1208     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1209 
1210     // Example string: "Today is a sunny day." in Japanese.
1211     const auto textBuf = utf8ToUtf16("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A");
1212 
1213     // In this test case, assign a replacement run for "\u6674\u5929" with 5 times of CHAR_WIDTH.
1214     auto doLineBreak = [=](float width) {
1215         MeasuredTextBuilder builder;
1216         builder.addCustomRun<ConstantRun>(Range(0, 3), "ja-JP", CHAR_WIDTH, ASCENT, DESCENT);
1217         builder.addReplacementRun(3, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("ja-JP"));
1218         builder.addCustomRun<ConstantRun>(Range(5, textBuf.size()), "ja-JP", CHAR_WIDTH, ASCENT,
1219                                           DESCENT);
1220 
1221         std::unique_ptr<MeasuredText> measuredText =
1222                 builder.build(textBuf, false /* compute hyphenation */,
1223                               false /* compute full layout */, nullptr /* no hint */);
1224         RectangleLineWidth rectangleLineWidth(width);
1225         TabStops tabStops(nullptr, 0, 0);
1226         return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
1227     };
1228 
1229     {
1230         constexpr float LINE_WIDTH = 100;
1231         // "\u6674\u5929" is a single replacement span. Do not break.
1232         // clang-format off
1233         std::vector<LineBreakExpectation> expect = {
1234                 {"\u672C\u65E5\u306F\u6674\u5929\u306A\u308A",
1235                   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1236         };
1237         // clang-format on
1238         const auto actual = doLineBreak(LINE_WIDTH);
1239         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1240                                                    << " vs " << std::endl
1241                                                    << toString(textBuf, actual);
1242     }
1243     {
1244         constexpr float LINE_WIDTH = 90;
1245         // "\u6674\u5929" is a single replacement span. Do not break.
1246         // clang-format off
1247         std::vector<LineBreakExpectation> expect = {
1248                 {"\u672C\u65E5\u306F\u6674\u5929\u306A",
1249                   90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1250                 {"\u308A",
1251                   10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1252         };
1253         // clang-format on
1254         const auto actual = doLineBreak(LINE_WIDTH);
1255         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1256                                                    << " vs " << std::endl
1257                                                    << toString(textBuf, actual);
1258     }
1259     {
1260         constexpr float LINE_WIDTH = 80;
1261         // "\u6674\u5929" is a single replacement span. Do not break.
1262         // clang-format off
1263         std::vector<LineBreakExpectation> expect = {
1264                 {"\u672C\u65E5\u306F\u6674\u5929",
1265                   80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1266                 {"\u306A\u308A",
1267                   20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1268         };
1269         // clang-format on
1270         const auto actual = doLineBreak(LINE_WIDTH);
1271         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1272                                                    << " vs " << std::endl
1273                                                    << toString(textBuf, actual);
1274     }
1275     {
1276         constexpr float LINE_WIDTH = 70;
1277         // "\u6674\u5929" is a single replacement span. Do not break.
1278         // clang-format off
1279         std::vector<LineBreakExpectation> expect = {
1280                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1281                 {"\u6674\u5929\u306A\u308A", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1282         };
1283         // clang-format on
1284         const auto actual = doLineBreak(LINE_WIDTH);
1285         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1286                                                    << " vs " << std::endl
1287                                                    << toString(textBuf, actual);
1288     }
1289     {
1290         constexpr float LINE_WIDTH = 60;
1291         // "\u6674\u5929" is a single replacement span. Do not break.
1292         // clang-format off
1293         std::vector<LineBreakExpectation> expect = {
1294                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1295                 {"\u6674\u5929\u306A", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1296                 {"\u308A",             10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1297         };
1298         // clang-format on
1299         const auto actual = doLineBreak(LINE_WIDTH);
1300         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1301                                                    << " vs " << std::endl
1302                                                    << toString(textBuf, actual);
1303     }
1304     {
1305         constexpr float LINE_WIDTH = 50;
1306         // "\u6674\u5929" is a single replacement span. Do not break.
1307         // clang-format off
1308         std::vector<LineBreakExpectation> expect = {
1309                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1310                 {"\u6674\u5929",       50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1311                 {"\u306A\u308A",       20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1312         };
1313         // clang-format on
1314         const auto actual = doLineBreak(LINE_WIDTH);
1315         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1316                                                    << " vs " << std::endl
1317                                                    << toString(textBuf, actual);
1318     }
1319     {
1320         constexpr float LINE_WIDTH = 40;
1321         // "\u6674\u5929" is a single replacement span. Do not break.
1322         // clang-format off
1323         std::vector<LineBreakExpectation> expect = {
1324                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1325                 {"\u6674\u5929",       50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1326                 {"\u306A\u308A",       20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1327         };
1328         // clang-format on
1329         const auto actual = doLineBreak(LINE_WIDTH);
1330         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1331                                                    << " vs " << std::endl
1332                                                    << toString(textBuf, actual);
1333     }
1334     {
1335         constexpr float LINE_WIDTH = 10;
1336         // "\u6674\u5929" is a single replacement span. Do not break.
1337         // clang-format off
1338         std::vector<LineBreakExpectation> expect = {
1339                 {"\u672C",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1340                 {"\u65E5",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1341                 {"\u306F",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1342                 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1343                 {"\u306A",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1344                 {"\u308A",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1345         };
1346         // clang-format on
1347         const auto actual = doLineBreak(LINE_WIDTH);
1348         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1349                                                    << " vs " << std::endl
1350                                                    << toString(textBuf, actual);
1351     }
1352 }
1353 
TEST_F(GreedyLineBreakerTest,testReplacementSpanNotBreakTest_with_punctuation)1354 TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation) {
1355     constexpr float CHAR_WIDTH = 10.0;
1356     constexpr bool DO_HYPHEN = true;  // Do hyphenation in this test case.
1357 
1358     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1359     constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
1360     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1361 
1362     const auto textBuf = utf8ToUtf16("This (is an) example text.");
1363 
1364     // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1365     auto doLineBreak = [=](float width) {
1366         MeasuredTextBuilder builder;
1367         builder.addCustomRun<ConstantRun>(Range(0, 6), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1368         builder.addReplacementRun(6, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1369         builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1370                                           DESCENT);
1371 
1372         std::unique_ptr<MeasuredText> measuredText =
1373                 builder.build(textBuf, false /* compute hyphenation */,
1374                               false /* compute full layout */, nullptr /* no hint */);
1375         RectangleLineWidth rectangleLineWidth(width);
1376         TabStops tabStops(nullptr, 0, 0);
1377         return breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
1378     };
1379 
1380     {
1381         constexpr float LINE_WIDTH = 1000;
1382         // "is an" is a single replacement span. Do not break.
1383         // clang-format off
1384         std::vector<LineBreakExpectation> expect = {
1385                 {"This (is an) example text.",
1386                   260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1387         };
1388         // clang-format on
1389         const auto actual = doLineBreak(LINE_WIDTH);
1390         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1391                                                    << " vs " << std::endl
1392                                                    << toString(textBuf, actual);
1393     }
1394     {
1395         constexpr float LINE_WIDTH = 250;
1396         // "is an" is a single replacement span. Do not break.
1397         // clang-format off
1398         std::vector<LineBreakExpectation> expect = {
1399                 {"This (is an) example ", 200, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1400                 {"text.",                  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1401         };
1402         // clang-format on
1403         const auto actual = doLineBreak(LINE_WIDTH);
1404         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1405                                                    << " vs " << std::endl
1406                                                    << toString(textBuf, actual);
1407     }
1408     {
1409         constexpr float LINE_WIDTH = 190;
1410         // "is an" is a single replacement span. Do not break.
1411         // clang-format off
1412         std::vector<LineBreakExpectation> expect = {
1413                 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1414                 {"example text.", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1415         };
1416         // clang-format on
1417         const auto actual = doLineBreak(LINE_WIDTH);
1418         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1419                                                    << " vs " << std::endl
1420                                                    << toString(textBuf, actual);
1421     }
1422     {
1423         constexpr float LINE_WIDTH = 120;
1424         // "is an" is a single replacement span. Do not break.
1425         // clang-format off
1426         std::vector<LineBreakExpectation> expect = {
1427                 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1428                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1429                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1430         };
1431         // clang-format on
1432         const auto actual = doLineBreak(LINE_WIDTH);
1433         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1434                                                    << " vs " << std::endl
1435                                                    << toString(textBuf, actual);
1436     }
1437     {
1438         constexpr float LINE_WIDTH = 110;
1439         // "is an" is a single replacement span. Do not break.
1440         // clang-format off
1441         std::vector<LineBreakExpectation> expect = {
1442                 {"This ",    40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1443                 {"(is an) ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1444                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1445                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1446         };
1447         // clang-format on
1448         const auto actual = doLineBreak(LINE_WIDTH);
1449         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1450                                                    << " vs " << std::endl
1451                                                    << toString(textBuf, actual);
1452     }
1453     {
1454         constexpr float LINE_WIDTH = 60;
1455         // "is an" is a single replacement span. Do not break.
1456         // clang-format off
1457         std::vector<LineBreakExpectation> expect = {
1458                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1459                 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1460                 {") ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1461                 {"exam-",  50, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT},
1462                 {"ple ",   30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1463                 {"text.",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1464         };
1465         // clang-format on
1466         const auto actual = doLineBreak(LINE_WIDTH);
1467         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1468                                                    << " vs " << std::endl
1469                                                    << toString(textBuf, actual);
1470     }
1471     {
1472         constexpr float LINE_WIDTH = 50;
1473         // "is an" is a single replacement span. Do not break.
1474         // clang-format off
1475         std::vector<LineBreakExpectation> expect = {
1476                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1477                 {"(",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1478                 {"is an",  50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1479                 {") ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1480                 {"exam-",  50, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT},
1481                 {"ple ",   30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1482                 {"text.",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1483         };
1484         // clang-format on
1485         const auto actual = doLineBreak(LINE_WIDTH);
1486         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1487                                                    << " vs " << std::endl
1488                                                    << toString(textBuf, actual);
1489     }
1490     {
1491         constexpr float LINE_WIDTH = 40;
1492         // "is an" is a single replacement span. Do not break.
1493         // clang-format off
1494         std::vector<LineBreakExpectation> expect = {
1495                 {"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1496                 {"(",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1497                 {"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1498                 {") ",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1499                 {"ex-",   30, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT},
1500                 {"am-",   30, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT},
1501                 {"ple ",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1502                 {"text",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1503                 {".",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1504         };
1505         // clang-format on
1506         const auto actual = doLineBreak(LINE_WIDTH);
1507         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1508                                                    << " vs " << std::endl
1509                                                    << toString(textBuf, actual);
1510     }
1511     {
1512         constexpr float LINE_WIDTH = 10;
1513         // "is an" is a single replacement span. Do not break.
1514         // clang-format off
1515         std::vector<LineBreakExpectation> expect = {
1516                 {"T",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1517                 {"h",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1518                 {"i",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1519                 {"s ",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1520                 {"(",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1521                 {"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1522                 {") ",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1523                 {"e",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1524                 {"x",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1525                 {"a",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1526                 {"m",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1527                 {"p",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1528                 {"l",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1529                 {"e ",    10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1530                 {"t",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1531                 {"e",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1532                 {"x",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1533                 {"t",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1534                 {".",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1535         };
1536         // clang-format on
1537         const auto actual = doLineBreak(LINE_WIDTH);
1538         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1539                                                    << " vs " << std::endl
1540                                                    << toString(textBuf, actual);
1541     }
1542 }
1543 
TEST_F(GreedyLineBreakerTest,testControllCharAfterSpace)1544 TEST_F(GreedyLineBreakerTest, testControllCharAfterSpace) {
1545     constexpr bool NO_HYPHEN = false;  // No hyphenation in this test case.
1546     const std::vector<uint16_t> textBuf = utf8ToUtf16("example \u2066example");
1547 
1548     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1549     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1550     {
1551         constexpr float LINE_WIDTH = 90;
1552         std::vector<LineBreakExpectation> expect = {
1553                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1554                 {"\u2066example", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1555         };
1556 
1557         const auto actual = doLineBreak(textBuf, NO_HYPHEN, LINE_WIDTH);
1558         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1559                                                    << " vs " << std::endl
1560                                                    << toString(textBuf, actual);
1561     }
1562 }
1563 }  // namespace
1564 }  // namespace minikin
1565