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