• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <memory>
18 
19 #include <gtest/gtest.h>
20 
21 #include "minikin/Hyphenator.h"
22 
23 #include "FileUtils.h"
24 #include "FontTestUtils.h"
25 #include "HyphenatorMap.h"
26 #include "LineBreakerTestHelper.h"
27 #include "LocaleListCache.h"
28 #include "MinikinInternal.h"
29 #include "OptimalLineBreaker.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 OptimalLineBreakerTest : public testing::Test {
51 public:
OptimalLineBreakerTest()52     OptimalLineBreakerTest() {}
53 
~OptimalLineBreakerTest()54     virtual ~OptimalLineBreakerTest() {}
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,BreakStrategy strategy,HyphenationFrequency frequency,float lineWidth)67     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, BreakStrategy strategy,
68                                 HyphenationFrequency frequency, float lineWidth) {
69         return doLineBreak(textBuffer, strategy, frequency, "en-US", lineWidth);
70     }
71 
doLineBreak(const U16StringPiece & textBuffer,BreakStrategy strategy,HyphenationFrequency frequency,const std::string & lang,float lineWidth)72     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, BreakStrategy strategy,
73                                 HyphenationFrequency frequency, const std::string& lang,
74                                 float lineWidth) {
75         MeasuredTextBuilder builder;
76         auto family1 = buildFontFamily("Ascii.ttf");
77         auto family2 = buildFontFamily("CustomExtent.ttf");
78         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
79         auto fc = std::make_shared<FontCollection>(families);
80         MinikinPaint paint(fc);
81         paint.size = 10.0f;  // Make 1em=1px
82         paint.localeListId = LocaleListCache::getId(lang);
83         builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
84         std::unique_ptr<MeasuredText> measuredText =
85                 builder.build(textBuffer, true /* compute hyphenation */,
86                               false /* compute full layout */, nullptr /* no hint */);
87         return doLineBreak(textBuffer, *measuredText, strategy, frequency, lineWidth);
88     }
89 
doLineBreak(const U16StringPiece & textBuffer,const MeasuredText & measuredText,BreakStrategy strategy,HyphenationFrequency frequency,float lineWidth)90     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, const MeasuredText& measuredText,
91                                 BreakStrategy strategy, HyphenationFrequency frequency,
92                                 float lineWidth) {
93         RectangleLineWidth rectangleLineWidth(lineWidth);
94         return breakLineOptimal(textBuffer, measuredText, rectangleLineWidth, strategy, frequency,
95                                 false /* justified */);
96     }
97 
98 private:
99     std::vector<uint8_t> mHyphenationPattern;
100 };
101 
TEST_F(OptimalLineBreakerTest,testBreakWithoutHyphenation)102 TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
103     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
104     constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
105     constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
106     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
107     const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
108 
109     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
110     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
111     constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
112     // Note that disable clang-format everywhere since aligned expectation is more readable.
113     {
114         constexpr float LINE_WIDTH = 1000;
115         std::vector<LineBreakExpectation> expect = {
116                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
117         };
118 
119         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
120         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
121                                                    << " vs " << std::endl
122                                                    << toString(textBuf, actual);
123         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
124         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
125                                                    << " vs " << std::endl
126                                                    << toString(textBuf, actual);
127         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
128         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
129                                                    << " vs " << std::endl
130                                                    << toString(textBuf, actual);
131         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
132         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
133                                                    << " vs " << std::endl
134                                                    << toString(textBuf, actual);
135     }
136     {
137         constexpr float LINE_WIDTH = 240;
138         std::vector<LineBreakExpectation> expect = {
139                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
140         };
141         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
142         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
143                                                    << " vs " << std::endl
144                                                    << toString(textBuf, actual);
145         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
146         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
147                                                    << " vs " << std::endl
148                                                    << toString(textBuf, actual);
149         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
150         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
151                                                    << " vs " << std::endl
152                                                    << toString(textBuf, actual);
153         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
154         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
155                                                    << " vs " << std::endl
156                                                    << toString(textBuf, actual);
157     }
158     {
159         constexpr float LINE_WIDTH = 230;
160         // clang-format off
161         std::vector<LineBreakExpectation> expect = {
162                 { "This is an example " , 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
163                 { "text."               ,  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
164         };
165         // clang-format on
166 
167         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
168         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
169                                                    << " vs " << std::endl
170                                                    << toString(textBuf, actual);
171         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
172         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
173                                                    << " vs " << std::endl
174                                                    << toString(textBuf, actual);
175 
176         // clang-format off
177         expect = {
178                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
179                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
180         };
181         // clang-format on
182         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
183         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
184                                                    << " vs " << std::endl
185                                                    << toString(textBuf, actual);
186         // clang-format off
187         expect = {
188                 { "This is an ex-" , 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
189                 { "ample text."    , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
190         };
191         // clang-format on
192         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
193         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
194                                                    << " vs " << std::endl
195                                                    << toString(textBuf, actual);
196     }
197     {
198         constexpr float LINE_WIDTH = 170;
199         // clang-format off
200         std::vector<LineBreakExpectation> expect = {
201                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
202                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
203         };
204         // clang-format on
205 
206         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
207         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
208                                                    << " vs " << std::endl
209                                                    << toString(textBuf, actual);
210         // clang-format off
211         expect = {
212                 { "This is an exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
213                 { "ple text."        ,  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
214         };
215         // clang-format on
216         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
217         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
218                                                    << " vs " << std::endl
219                                                    << toString(textBuf, actual);
220         // clang-format off
221         expect = {
222                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
223                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
224         };
225         // clang-format on
226         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
227         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
228                                                    << " vs " << std::endl
229                                                    << toString(textBuf, actual);
230         // clang-format off
231         expect = {
232                 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
233                 { "ample text."   , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
234         };
235         // clang-format on
236         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
237         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
238                                                    << " vs " << std::endl
239                                                    << toString(textBuf, actual);
240     }
241     {
242         constexpr float LINE_WIDTH = 160;
243         // clang-format off
244         std::vector<LineBreakExpectation> expect = {
245                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
246                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
247         };
248         // clang-format on
249 
250         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
251         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
252                                                    << " vs " << std::endl
253                                                    << toString(textBuf, actual);
254         // clang-format off
255         expect = {
256                 { "This is an exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
257                 { "ple text."        ,  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
258         };
259         // clang-format on
260         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
261         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
262                                                    << " vs " << std::endl
263                                                    << toString(textBuf, actual);
264         // clang-format off
265         expect = {
266                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
267                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
268         };
269         // clang-format on
270         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
271         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
272                                                    << " vs " << std::endl
273                                                    << toString(textBuf, actual);
274         // clang-format off
275         expect = {
276                 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
277                 { "ample text."   , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
278         };
279         // clang-format on
280         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
281         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
282                                                    << " vs " << std::endl
283                                                    << toString(textBuf, actual);
284     }
285     {
286         constexpr float LINE_WIDTH = 150;
287         // clang-format off
288         std::vector<LineBreakExpectation> expect = {
289                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
290                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
291         };
292         // clang-format on
293 
294         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
295         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
296                                                    << " vs " << std::endl
297                                                    << toString(textBuf, actual);
298         // clang-format off
299         expect = {
300                 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
301                 { "ample text."   , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
302         };
303         // clang-format on
304         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
305         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
306                                                    << " vs " << std::endl
307                                                    << toString(textBuf, actual);
308         // clang-format off
309         expect = {
310                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
311                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
312         };
313         // clang-format on
314         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
315         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
316                                                    << " vs " << std::endl
317                                                    << toString(textBuf, actual);
318         // clang-format off
319         expect = {
320                 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
321                 { "ample text."   , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
322         };
323         // clang-format on
324         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
325         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
326                                                    << " vs " << std::endl
327                                                    << toString(textBuf, actual);
328     }
329     {
330         constexpr float LINE_WIDTH = 130;
331         // clang-format off
332         std::vector<LineBreakExpectation> expect = {
333                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
334                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
335         };
336         // clang-format on
337 
338         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
339         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
340                                                    << " vs " << std::endl
341                                                    << toString(textBuf, actual);
342         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
343         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
344                                                    << " vs " << std::endl
345                                                    << toString(textBuf, actual);
346         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
347         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
348                                                    << " vs " << std::endl
349                                                    << toString(textBuf, actual);
350         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
351         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
352                                                    << " vs " << std::endl
353                                                    << toString(textBuf, actual);
354     }
355     {
356         constexpr float LINE_WIDTH = 120;
357         // clang-format off
358         std::vector<LineBreakExpectation> expect = {
359                 { "This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
360                 { "example "   ,  70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
361                 { "text."      ,  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
362         };
363         // clang-format on
364 
365         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
366         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
367                                                    << " vs " << std::endl
368                                                    << toString(textBuf, actual);
369         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
370         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
371                                                    << " vs " << std::endl
372                                                    << toString(textBuf, actual);
373         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
374         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
375                                                    << " vs " << std::endl
376                                                    << toString(textBuf, actual);
377         // clang-format off
378         expect = {
379                 { "This is " ,  70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
380                 { "an exam-" ,  80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
381                 { "ple text.",  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
382         };
383         // clang-format on
384         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
385         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
386                                                    << " vs " << std::endl
387                                                    << toString(textBuf, actual);
388     }
389     {
390         constexpr float LINE_WIDTH = 90;
391         // clang-format off
392         std::vector<LineBreakExpectation> expect = {
393                 { "This "   , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
394                 { "is an "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
395                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
396                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
397         };
398         // clang-format on
399 
400         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
401         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
402                                                    << " vs " << std::endl
403                                                    << toString(textBuf, actual);
404         // clang-format off
405         expect = {
406                 { "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
407                 { "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
408                 { "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
409         };
410         // clang-format on
411         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
412         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
413                                                    << " vs " << std::endl
414                                                    << toString(textBuf, actual);
415         // clang-format off
416         expect = {
417                 { "This "   , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
418                 { "is an "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
419                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
420                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
421         };
422         // clang-format on
423         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
424         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
425                                                    << " vs " << std::endl
426                                                    << toString(textBuf, actual);
427         // clang-format off
428         expect = {
429                 { "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
430                 { "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
431                 { "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
432         };
433         // clang-format on
434         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
435         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
436                                                    << " vs " << std::endl
437                                                    << toString(textBuf, actual);
438     }
439     {
440         constexpr float LINE_WIDTH = 80;
441         // clang-format off
442         std::vector<LineBreakExpectation> expect = {
443                 { "This "   , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
444                 { "is an "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
445                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
446                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
447         };
448         // clang-format on
449 
450         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
451         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
452                                                    << " vs " << std::endl
453                                                    << toString(textBuf, actual);
454         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
455         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
456                                                    << " vs " << std::endl
457                                                    << toString(textBuf, actual);
458         // clang-format off
459         expect = {
460                 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
461                 { "an ex-"  , 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
462                 { "ample "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
463                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
464         };
465         // clang-format on
466         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
467         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
468                                                    << " vs " << std::endl
469                                                    << toString(textBuf, actual);
470         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
471         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
472                                                    << " vs " << std::endl
473                                                    << toString(textBuf, actual);
474     }
475     {
476         constexpr float LINE_WIDTH = 70;
477         // clang-format off
478         std::vector<LineBreakExpectation> expect = {
479                 { "This "   , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
480                 { "is an "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
481                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
482                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
483         };
484         // clang-format on
485 
486         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
487         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
488                                                    << " vs " << std::endl
489                                                    << toString(textBuf, actual);
490         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
491         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
492                                                    << " vs " << std::endl
493                                                    << toString(textBuf, actual);
494         // clang-format off
495         expect = {
496                 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
497                 { "an ex-"  , 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
498                 { "ample "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
499                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
500         };
501         // clang-format on
502         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
503         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
504                                                    << " vs " << std::endl
505                                                    << toString(textBuf, actual);
506         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
507         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
508                                                    << " vs " << std::endl
509                                                    << toString(textBuf, actual);
510     }
511     {
512         constexpr float LINE_WIDTH = 60;
513         // clang-format off
514         std::vector<LineBreakExpectation> expect = {
515                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
516                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
517                 // TODO: Is this desperate break working correctly?
518                 { "exa"   , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
519                 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
520                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
521         };
522         // clang-format on
523 
524         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
525         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
526                                                    << " vs " << std::endl
527                                                    << toString(textBuf, actual);
528         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
529         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
530                                                    << " vs " << std::endl
531                                                    << toString(textBuf, actual);
532         // clang-format off
533         expect = {
534                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
535                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
536                 { "exam-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
537                 { "ple "  , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
538                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
539         };
540         // clang-format on
541         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
542         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
543                                                    << " vs " << std::endl
544                                                    << toString(textBuf, actual);
545         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
546         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
547                                                    << " vs " << std::endl
548                                                    << toString(textBuf, actual);
549     }
550     {
551         constexpr float LINE_WIDTH = 50;
552         // clang-format off
553         std::vector<LineBreakExpectation> expect = {
554                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
555                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
556                 // TODO: Is this desperate break working correctly?
557                 { "exa"   , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
558                 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
559                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
560         };
561         // clang-format on
562 
563         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
564         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
565                                                    << " vs " << std::endl
566                                                    << toString(textBuf, actual);
567         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
568         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
569                                                    << " vs " << std::endl
570                                                    << toString(textBuf, actual);
571         // clang-format off
572         expect = {
573                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
574                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
575                 { "exam-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
576                 { "ple "  , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
577                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
578         };
579         // clang-format on
580         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
581         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
582                                                    << " vs " << std::endl
583                                                    << toString(textBuf, actual);
584         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
585         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
586                                                    << " vs " << std::endl
587                                                    << toString(textBuf, actual);
588     }
589     {
590         constexpr float LINE_WIDTH = 40;
591         // clang-format off
592         std::vector<LineBreakExpectation> expect = {
593                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
594                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
595                 { "an "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
596                 // TODO: Is this desperate break working correctly?
597                 { "exa"   , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
598                 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
599                 { "text"  , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
600                 { "."     , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
601         };
602         // clang-format on
603 
604         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
605         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
606                                                    << " vs " << std::endl
607                                                    << toString(textBuf, actual);
608         // clang-format off
609         expect = {
610                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
611                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
612                 { "an "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
613                 // TODO: Is this desperate break working correctly?
614                 { "exa"   , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
615                 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
616                 // TODO: Is this desperate break working correctly?
617                 { "t"     , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
618                 { "ext."  , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
619         };
620         // clang-format on
621         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
622         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
623                                                    << " vs " << std::endl
624                                                    << toString(textBuf, actual);
625         // clang-format off
626         expect = {
627                 { "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
628                 { "is "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
629                 { "an "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
630                 { "ex-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
631                 { "am-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
632                 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
633                 { "text" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
634                 { "."    , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
635         };
636         // clang-format on
637         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
638         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
639                                                    << " vs " << std::endl
640                                                    << toString(textBuf, actual);
641         // clang-format off
642         expect = {
643                 { "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
644                 { "is "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
645                 { "an "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
646                 { "ex-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
647                 { "am-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
648                 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
649                 // TODO: Is this desperate break working correctly?
650                 { "te"   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
651                 { "xt."  , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
652         };
653         // clang-format on
654         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
655         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
656                                                    << " vs " << std::endl
657                                                    << toString(textBuf, actual);
658     }
659     {
660         constexpr float LINE_WIDTH = 30;
661         // clang-format off
662         std::vector<LineBreakExpectation> expect = {
663                 // TODO: Is this desperate break working correctly?
664                 { "T"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
665                 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
666                 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
667                 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
668                 // TODO: Is this desperate break working correctly?
669                 { "e"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
670                 { "xam" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
671                 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
672                 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
673                 { "t."  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
674         };
675         // clang-format on
676 
677         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
678         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
679                                                    << " vs " << std::endl
680                                                    << toString(textBuf, actual);
681         // clang-format off
682         expect = {
683                 // TODO: Is this desperate break working correctly?
684                 { "T"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
685                 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
686                 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
687                 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
688                 // TODO: Is this desperate break working correctly?
689                 { "e"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
690                 { "xam" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
691                 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
692                 // TODO: Is this desperate break working correctly?
693                 { "te"  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
694                 { "xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
695         };
696         // clang-format on
697         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
698         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
699                                                    << " vs " << std::endl
700                                                    << toString(textBuf, actual);
701         // clang-format off
702         expect = {
703                 // TODO: Is this desperate break working correctly?
704                 { "T"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
705                 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
706                 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
707                 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
708                 { "ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
709                 { "am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
710                 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
711                 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
712                 { "t."  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
713         };
714         // clang-format on
715         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
716         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
717                                                    << " vs " << std::endl
718                                                    << toString(textBuf, actual);
719         // clang-format off
720         expect = {
721                 // TODO: Is this desperate break working correctly?
722                 {"T"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
723                 {"his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
724                 {"is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
725                 {"an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
726                 {"ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
727                 {"am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
728                 {"ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
729                 // TODO: Is this desperate break working correctly?
730                 {"te"  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
731                 {"xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
732         };
733         // clang-format on
734         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
735         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
736                                                    << " vs " << std::endl
737                                                    << toString(textBuf, actual);
738     }
739     {
740         constexpr float LINE_WIDTH = 20;
741         // clang-format off
742         std::vector<LineBreakExpectation> expect = {
743                 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
744                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
745                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
746                 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
747                 // TODO: Is this desperate break working correctly?
748                 { "e"  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
749                 { "xa" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
750                 { "mp" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
751                 { "le ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
752                 { "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
753                 { "xt" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
754                 { "."  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
755         };
756         // clang-format on
757 
758         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
759         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
760                                                    << " vs " << std::endl
761                                                    << toString(textBuf, actual);
762         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
763         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
764                                                    << " vs " << std::endl
765                                                    << toString(textBuf, actual);
766         // clang-format off
767         expect = {
768                 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
769                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
770                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
771                 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
772                 // TODO: Is this desperate break working correctly?
773                 { "e"  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
774                 { "xa" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
775                 { "mp" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
776                 { "le ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
777                 // TODO: Is this desperate break working correctly?
778                 { "t"  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
779                 { "ex" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
780                 { "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
781         };
782         // clang-format on
783         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
784         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
785                                                    << " vs " << std::endl
786                                                    << toString(textBuf, actual);
787         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
788         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
789                                                    << " vs " << std::endl
790                                                    << toString(textBuf, actual);
791     }
792     {
793         constexpr float LINE_WIDTH = 10;
794         // clang-format off
795         std::vector<LineBreakExpectation> expect = {
796                 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
797                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
798                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
799                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
800                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
801                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
802                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
803                 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
804                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
805                 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
806                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
807                 { "m" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
808                 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
809                 { "l" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
810                 { "e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
811                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
812                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
813                 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
814                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
815                 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
816         };
817         // clang-format on
818 
819         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
820         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
821                                                    << " vs " << std::endl
822                                                    << toString(textBuf, actual);
823         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
824         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
825                                                    << " vs " << std::endl
826                                                    << toString(textBuf, actual);
827         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
828         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
829                                                    << " vs " << std::endl
830                                                    << toString(textBuf, actual);
831         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
832         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
833                                                    << " vs " << std::endl
834                                                    << toString(textBuf, actual);
835     }
836 }
837 
TEST_F(OptimalLineBreakerTest,testHyphenationStartLineChange)838 TEST_F(OptimalLineBreakerTest, testHyphenationStartLineChange) {
839     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
840     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
841     // "hyphenation" is hyphnated to "hy-phen-a-tion".
842     const std::vector<uint16_t> textBuf = utf8ToUtf16("czerwono-niebieska");
843 
844     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
845     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
846     constexpr StartHyphenEdit START_HYPHEN = StartHyphenEdit::INSERT_HYPHEN;
847 
848     // Note that disable clang-format everywhere since aligned expectation is more readable.
849     {
850         constexpr float LINE_WIDTH = 1000;
851         std::vector<LineBreakExpectation> expect = {
852                 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
853         };
854 
855         const auto actual =
856                 doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
857         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
858                                                    << " vs " << std::endl
859                                                    << toString(textBuf, actual);
860     }
861     {
862         constexpr float LINE_WIDTH = 180;
863         std::vector<LineBreakExpectation> expect = {
864                 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
865         };
866 
867         const auto actual =
868                 doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
869         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
870                                                    << " vs " << std::endl
871                                                    << toString(textBuf, actual);
872     }
873     {
874         constexpr float LINE_WIDTH = 130;
875         // clang-format off
876         std::vector<LineBreakExpectation> expect = {
877                 {"czerwono-" ,  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
878                 {"-niebieska", 100,    START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
879         };
880         // clang-format on
881 
882         const auto actual =
883                 doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
884         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
885                                                    << " vs " << std::endl
886                                                    << toString(textBuf, actual);
887     }
888 }
889 
TEST_F(OptimalLineBreakerTest,testZeroWidthLine)890 TEST_F(OptimalLineBreakerTest, testZeroWidthLine) {
891     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
892     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
893     constexpr float LINE_WIDTH = 0;
894 
895     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
896     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
897 
898     {
899         const auto textBuf = utf8ToUtf16("");
900         std::vector<LineBreakExpectation> expect = {};
901         const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
902         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
903                                                    << " vs " << std::endl
904                                                    << toString(textBuf, actual);
905     }
906     {
907         const auto textBuf = utf8ToUtf16("A");
908         std::vector<LineBreakExpectation> expect = {
909                 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
910         };
911         const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
912         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
913                                                    << " vs " << std::endl
914                                                    << toString(textBuf, actual);
915     }
916     {
917         const auto textBuf = utf8ToUtf16("AB");
918         std::vector<LineBreakExpectation> expect = {
919                 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
920                 {"B", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
921         };
922         const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
923         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
924                                                    << " vs " << std::endl
925                                                    << toString(textBuf, actual);
926     }
927 }
928 
TEST_F(OptimalLineBreakerTest,testZeroWidthCharacter)929 TEST_F(OptimalLineBreakerTest, testZeroWidthCharacter) {
930     constexpr float CHAR_WIDTH = 0.0;
931     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
932     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
933 
934     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
935     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
936     {
937         constexpr float LINE_WIDTH = 1.0;
938         const auto textBuf = utf8ToUtf16("This is an example text.");
939         std::vector<LineBreakExpectation> expect = {
940                 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
941         };
942         MeasuredTextBuilder builder;
943         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
944                                           DESCENT);
945         std::unique_ptr<MeasuredText> measuredText =
946                 builder.build(textBuf, true /* compute hyphenation */,
947                               false /* compute full layout */, nullptr /* no hint */);
948 
949         const auto actual =
950                 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
951         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
952                                                    << " vs " << std::endl
953                                                    << toString(textBuf, actual);
954     }
955     {
956         constexpr float LINE_WIDTH = 0.0;
957         const auto textBuf = utf8ToUtf16("This is an example text.");
958         std::vector<LineBreakExpectation> expect = {
959                 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
960         };
961         MeasuredTextBuilder builder;
962         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
963                                           DESCENT);
964         std::unique_ptr<MeasuredText> measuredText =
965                 builder.build(textBuf, true /* compute hyphenation */,
966                               false /* compute full layout */, nullptr /* no hint */);
967 
968         const auto actual =
969                 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
970         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
971                                                    << " vs " << std::endl
972                                                    << toString(textBuf, actual);
973     }
974 }
975 
TEST_F(OptimalLineBreakerTest,testLocaleSwitchTest)976 TEST_F(OptimalLineBreakerTest, testLocaleSwitchTest) {
977     constexpr float CHAR_WIDTH = 10.0;
978     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
979     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
980 
981     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
982     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
983 
984     constexpr float LINE_WIDTH = 240;
985     const auto textBuf = utf8ToUtf16("This is an example text.");
986     {
987         std::vector<LineBreakExpectation> expect = {
988                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
989         };
990 
991         MeasuredTextBuilder builder;
992         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
993         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
994                                           DESCENT);
995         std::unique_ptr<MeasuredText> measuredText =
996                 builder.build(textBuf, true /* compute hyphenation */,
997                               false /* compute full layout */, nullptr /* no hint */);
998 
999         const auto actual =
1000                 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1001         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1002                                                    << " vs " << std::endl
1003                                                    << toString(textBuf, actual);
1004     }
1005     {
1006         std::vector<LineBreakExpectation> expect = {
1007                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1008         };
1009 
1010         MeasuredTextBuilder builder;
1011         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1012         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1013                                           DESCENT);
1014         std::unique_ptr<MeasuredText> measuredText =
1015                 builder.build(textBuf, true /* compute hyphenation */,
1016                               false /* compute full layout */, nullptr /* no hint */);
1017         const auto actual =
1018                 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1019         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1020                                                    << " vs " << std::endl
1021                                                    << toString(textBuf, actual);
1022     }
1023 }
1024 
TEST_F(OptimalLineBreakerTest,testEmailOrUrl)1025 TEST_F(OptimalLineBreakerTest, testEmailOrUrl) {
1026     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1027     constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
1028     constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
1029     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
1030 
1031     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1032     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1033     {
1034         constexpr float LINE_WIDTH = 240;
1035         const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
1036         // clang-format off
1037         std::vector<LineBreakExpectation> expect = {
1038                 // TODO: Fix this. Prefer not to break inside URL.
1039                 {"This is an url: http://a", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1040                 {".b",                        20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1041         };
1042         // clang-format on
1043         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1044         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1045                                                    << " vs " << std::endl
1046                                                    << toString(textBuf, actual);
1047         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1048         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1049                                                    << " vs " << std::endl
1050                                                    << toString(textBuf, actual);
1051         // clang-format off
1052         expect = {
1053                 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1054                 {"http://a.b",       100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1055         };
1056         // clang-format on
1057         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1058         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1059                                                    << " vs " << std::endl
1060                                                    << toString(textBuf, actual);
1061         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1062         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1063                                                    << " vs " << std::endl
1064                                                    << toString(textBuf, actual);
1065     }
1066     {
1067         constexpr float LINE_WIDTH = 240;
1068         const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
1069         // clang-format off
1070         std::vector<LineBreakExpectation> expect = {
1071                 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1072                 {"a@example.com"     , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1073         };
1074         // clang-format on
1075 
1076         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1077         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1078                                                    << " vs " << std::endl
1079                                                    << toString(textBuf, actual);
1080         actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1081         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1082                                                    << " vs " << std::endl
1083                                                    << toString(textBuf, actual);
1084         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1085         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1086                                                    << " vs " << std::endl
1087                                                    << toString(textBuf, actual);
1088         actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1089         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1090                                                    << " vs " << std::endl
1091                                                    << toString(textBuf, actual);
1092     }
1093 }
1094 
TEST_F(OptimalLineBreakerTest,testLocaleSwitch_InEmailOrUrl)1095 TEST_F(OptimalLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
1096     constexpr float CHAR_WIDTH = 10.0;
1097     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1098     constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
1099     constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
1100     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
1101 
1102     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1103     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1104 
1105     constexpr float LINE_WIDTH = 240;
1106     {
1107         const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
1108         MeasuredTextBuilder builder;
1109         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1110         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1111                                           DESCENT);
1112         std::unique_ptr<MeasuredText> measured =
1113                 builder.build(textBuf, true /* compute hyphenation */,
1114                               false /* compute full layout */, nullptr /* no hint */);
1115 
1116         // clang-format off
1117         std::vector<LineBreakExpectation> expect = {
1118                 // TODO: Fix this. Prefer not to break inside URL.
1119                 {"This is an url: http://a", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1120                 {".b",                        20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1121         };
1122         // clang-format on
1123 
1124         auto actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1125         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1126                                                    << " vs " << std::endl
1127                                                    << toString(textBuf, actual);
1128         actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1129         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1130                                                    << " vs " << std::endl
1131                                                    << toString(textBuf, actual);
1132 
1133         // clang-format off
1134         expect = {
1135                 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1136                 {"http://a.b",       100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1137         };
1138         // clang-format on
1139 
1140         actual = doLineBreak(textBuf, *measured, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1141         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1142                                                    << " vs " << std::endl
1143                                                    << toString(textBuf, actual);
1144         actual = doLineBreak(textBuf, *measured, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1145         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1146                                                    << " vs " << std::endl
1147                                                    << toString(textBuf, actual);
1148     }
1149     {
1150         const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
1151         MeasuredTextBuilder builder;
1152         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1153         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1154                                           DESCENT);
1155         std::unique_ptr<MeasuredText> measured =
1156                 builder.build(textBuf, true /* compute hyphenation */,
1157                               false /* compute full layout */, nullptr /* no hint */);
1158 
1159         // clang-format off
1160         std::vector<LineBreakExpectation> expect = {
1161                 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1162                 {"a@example.com",      130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1163         };
1164         // clang-format on
1165 
1166         auto actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1167         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1168                                                    << " vs " << std::endl
1169                                                    << toString(textBuf, actual);
1170         actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1171         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1172                                                    << " vs " << std::endl
1173                                                    << toString(textBuf, actual);
1174         actual = doLineBreak(textBuf, *measured, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1175         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1176                                                    << " vs " << std::endl
1177                                                    << toString(textBuf, actual);
1178         actual = doLineBreak(textBuf, *measured, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1179         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1180                                                    << " vs " << std::endl
1181                                                    << toString(textBuf, actual);
1182     }
1183 }
1184 
TEST_F(OptimalLineBreakerTest,ExtentTest)1185 TEST_F(OptimalLineBreakerTest, ExtentTest) {
1186     constexpr HyphenationFrequency NO_HYPHEN = HyphenationFrequency::None;
1187     const std::vector<uint16_t> textBuf = utf8ToUtf16("The \u3042\u3044\u3046 is Japanese.");
1188 
1189     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1190     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1191     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1192     {
1193         constexpr float LINE_WIDTH = 1000;
1194         std::vector<LineBreakExpectation> expect = {
1195                 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
1196                  CUSTOM_ASCENT, CUSTOM_DESCENT},
1197         };
1198 
1199         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1200         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1201                                                    << " vs " << std::endl
1202                                                    << toString(textBuf, actual);
1203     }
1204     {
1205         constexpr float LINE_WIDTH = 200;
1206         std::vector<LineBreakExpectation> expect = {
1207                 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
1208                  CUSTOM_ASCENT, CUSTOM_DESCENT},
1209         };
1210 
1211         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1212         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1213                                                    << " vs " << std::endl
1214                                                    << toString(textBuf, actual);
1215     }
1216     {
1217         constexpr float LINE_WIDTH = 190;
1218         std::vector<LineBreakExpectation> expect = {
1219                 {"The \u3042\u3044\u3046 is ", 100, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1220                  CUSTOM_DESCENT},
1221                 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1222         };
1223         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1224         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1225                                                    << " vs " << std::endl
1226                                                    << toString(textBuf, actual);
1227     }
1228     {
1229         constexpr float LINE_WIDTH = 90;
1230         std::vector<LineBreakExpectation> expect = {
1231                 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1232                 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1233                  CUSTOM_DESCENT},
1234                 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1235         };
1236         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1237         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1238                                                    << " vs " << std::endl
1239                                                    << toString(textBuf, actual);
1240     }
1241     {
1242         constexpr float LINE_WIDTH = 50;
1243         std::vector<LineBreakExpectation> expect = {
1244                 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1245                 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1246                  CUSTOM_DESCENT},
1247                 {"Japan", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1248                 {"ese.", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1249         };
1250         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1251         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1252                                                    << " vs " << std::endl
1253                                                    << toString(textBuf, actual);
1254     }
1255     {
1256         constexpr float LINE_WIDTH = 40;
1257         std::vector<LineBreakExpectation> expect = {
1258                 {"The ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1259                 {"\u3042\u3044", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1260                 {"\u3046 is ", 40, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1261                 {"Japa", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1262                 {"nese", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1263                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1264         };
1265         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, 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 = 20;
1272         std::vector<LineBreakExpectation> expect = {
1273                 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1274                 {"he ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1275                 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1276                 {"\u3044\u3046 ", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1277                  CUSTOM_DESCENT},
1278                 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1279                 {"Ja", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1280                 {"pa", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1281                 {"ne", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1282                 {"se", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1283                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1284         };
1285         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1286         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1287                                                    << " vs " << std::endl
1288                                                    << toString(textBuf, actual);
1289     }
1290     {
1291         constexpr float LINE_WIDTH = 10;
1292         std::vector<LineBreakExpectation> expect = {
1293                 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1294                 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1295                 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1296                 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1297                 {"\u3044", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1298                 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1299                 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1300                 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1301                 {"J", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1302                 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1303                 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1304                 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1305                 {"n", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1306                 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1307                 {"s", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1308                 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1309                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1310         };
1311         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
1312         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1313                                                    << " vs " << std::endl
1314                                                    << toString(textBuf, actual);
1315     }
1316 }
1317 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_SingleChar)1318 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
1319     constexpr float CHAR_WIDTH = 10.0;
1320 
1321     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1322     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1323 
1324     const auto textBuf = utf8ToUtf16("This is an example \u2639 text.");
1325 
1326     // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1327     auto doLineBreak = [=](float width) {
1328         MeasuredTextBuilder builder;
1329         builder.addCustomRun<ConstantRun>(Range(0, 19), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1330         builder.addReplacementRun(19, 21, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1331         builder.addCustomRun<ConstantRun>(Range(21, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1332                                           DESCENT);
1333 
1334         std::unique_ptr<MeasuredText> measuredText =
1335                 builder.build(textBuf, false /* compute hyphenation */,
1336                               false /* compute full layout */, nullptr /* no hint */);
1337         RectangleLineWidth rectangleLineWidth(width);
1338         TabStops tabStops(nullptr, 0, 0);
1339         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1340                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1341                                 false /* justified */);
1342     };
1343 
1344     {
1345         constexpr float LINE_WIDTH = 100;
1346         // "is an" is a single replacement span. Do not break.
1347         // clang-format off
1348         std::vector<LineBreakExpectation> expect = {
1349                 {"This is an ",   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1350                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1351                 {"\u2639 text.",  100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1352         };
1353         // clang-format on
1354         const auto actual = doLineBreak(LINE_WIDTH);
1355         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1356                                                    << " vs " << std::endl
1357                                                    << toString(textBuf, actual);
1358     }
1359     {
1360         constexpr float LINE_WIDTH = 90;
1361         // "is an" is a single replacement span. Do not break.
1362         // clang-format off
1363         std::vector<LineBreakExpectation> expect = {
1364                 {"This ",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1365                 {"is an ",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1366                 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1367                 {"\u2639 ", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1368                 {"text.",   50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1369         };
1370         // clang-format on
1371         const auto actual = doLineBreak(LINE_WIDTH);
1372         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1373                                                    << " vs " << std::endl
1374                                                    << toString(textBuf, actual);
1375     }
1376     {
1377         constexpr float LINE_WIDTH = 10;
1378         // "is an" is a single replacement span. Do not break.
1379         // clang-format off
1380         std::vector<LineBreakExpectation> expect = {
1381                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1382                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1383                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1384                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1385                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1386                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1387                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1388                 {"n ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1389                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1390                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1391                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1392                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1393                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1394                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1395                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1396                 {"\u2639 ",50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1397                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1398                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1399                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1400                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1401                 {".",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1402         };
1403         // clang-format on
1404         const auto actual = doLineBreak(LINE_WIDTH);
1405         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1406                                                    << " vs " << std::endl
1407                                                    << toString(textBuf, actual);
1408     }
1409 }
1410 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_MultipleChars)1411 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
1412     constexpr float CHAR_WIDTH = 10.0;
1413 
1414     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1415     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1416 
1417     const auto textBuf = utf8ToUtf16("This is an example text.");
1418 
1419     // In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
1420     auto doLineBreak = [=](float width) {
1421         MeasuredTextBuilder builder;
1422         builder.addCustomRun<ConstantRun>(Range(0, 5), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1423         builder.addReplacementRun(5, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1424         builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1425                                           DESCENT);
1426 
1427         std::unique_ptr<MeasuredText> measuredText =
1428                 builder.build(textBuf, false /* compute hyphenation */,
1429                               false /* compute full layout */, nullptr /* no hint */);
1430         RectangleLineWidth rectangleLineWidth(width);
1431         TabStops tabStops(nullptr, 0, 0);
1432         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1433                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1434                                 false /* justified */);
1435     };
1436 
1437     {
1438         constexpr float LINE_WIDTH = 100;
1439         // "is an" is a single replacement span. Do not break.
1440         // clang-format off
1441         std::vector<LineBreakExpectation> expect = {
1442                 {"This is an ",   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1443                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1444                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1445         };
1446         // clang-format on
1447         const auto actual = doLineBreak(LINE_WIDTH);
1448         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1449                                                    << " vs " << std::endl
1450                                                    << toString(textBuf, actual);
1451     }
1452     {
1453         constexpr float LINE_WIDTH = 90;
1454         // "is an" is a single replacement span. Do not break.
1455         // clang-format off
1456         std::vector<LineBreakExpectation> expect = {
1457                 {"This ",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1458                 {"is an ",  50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1459                 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1460                 {"text.",   50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1461         };
1462         // clang-format on
1463         const auto actual = doLineBreak(LINE_WIDTH);
1464         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1465                                                    << " vs " << std::endl
1466                                                    << toString(textBuf, actual);
1467     }
1468     {
1469         constexpr float LINE_WIDTH = 10;
1470         // "is an" is a single replacement span. Do not break.
1471         // clang-format off
1472         std::vector<LineBreakExpectation> expect = {
1473                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1474                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1475                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1476                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1477                 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1478                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1479                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1480                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1481                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1482                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1483                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1484                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1485                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1486                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1487                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1488                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1489                 {".",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1490         };
1491         // clang-format on
1492         const auto actual = doLineBreak(LINE_WIDTH);
1493         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1494                                                    << " vs " << std::endl
1495                                                    << toString(textBuf, actual);
1496     }
1497 }
1498 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_CJK)1499 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
1500     constexpr float CHAR_WIDTH = 10.0;
1501 
1502     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1503     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1504 
1505     // Example string: "Today is a sunny day." in Japanese.
1506     const auto textBuf = utf8ToUtf16("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A");
1507 
1508     // In this test case, assign a replacement run for "\u6674\u5929" with 5 times of CHAR_WIDTH.
1509     auto doLineBreak = [=](float width) {
1510         MeasuredTextBuilder builder;
1511         builder.addCustomRun<ConstantRun>(Range(0, 3), "ja-JP", CHAR_WIDTH, ASCENT, DESCENT);
1512         builder.addReplacementRun(3, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("ja-JP"));
1513         builder.addCustomRun<ConstantRun>(Range(5, textBuf.size()), "ja-JP", CHAR_WIDTH, ASCENT,
1514                                           DESCENT);
1515 
1516         std::unique_ptr<MeasuredText> measuredText =
1517                 builder.build(textBuf, false /* compute hyphenation */,
1518                               false /* compute full layout */, nullptr /* no hint */);
1519         RectangleLineWidth rectangleLineWidth(width);
1520         TabStops tabStops(nullptr, 0, 0);
1521         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1522                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1523                                 false /* justified */);
1524     };
1525 
1526     {
1527         constexpr float LINE_WIDTH = 100;
1528         // "\u6674\u5929" is a single replacement span. Do not break.
1529         // clang-format off
1530         std::vector<LineBreakExpectation> expect = {
1531                 {"\u672C\u65E5\u306F\u6674\u5929\u306A\u308A",
1532                   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1533         };
1534         // clang-format on
1535         const auto actual = doLineBreak(LINE_WIDTH);
1536         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1537                                                    << " vs " << std::endl
1538                                                    << toString(textBuf, actual);
1539     }
1540     {
1541         constexpr float LINE_WIDTH = 90;
1542         // "\u6674\u5929" is a single replacement span. Do not break.
1543         // clang-format off
1544         std::vector<LineBreakExpectation> expect = {
1545                 {"\u672C\u65E5\u306F\u6674\u5929\u306A",
1546                   90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1547                 {"\u308A",
1548                   10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1549         };
1550         // clang-format on
1551         const auto actual = doLineBreak(LINE_WIDTH);
1552         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1553                                                    << " vs " << std::endl
1554                                                    << toString(textBuf, actual);
1555     }
1556     {
1557         constexpr float LINE_WIDTH = 80;
1558         // "\u6674\u5929" is a single replacement span. Do not break.
1559         // clang-format off
1560         std::vector<LineBreakExpectation> expect = {
1561                 {"\u672C\u65E5\u306F\u6674\u5929",
1562                   80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1563                 {"\u306A\u308A",
1564                   20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1565         };
1566         // clang-format on
1567         const auto actual = doLineBreak(LINE_WIDTH);
1568         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1569                                                    << " vs " << std::endl
1570                                                    << toString(textBuf, actual);
1571     }
1572     {
1573         constexpr float LINE_WIDTH = 70;
1574         // "\u6674\u5929" is a single replacement span. Do not break.
1575         // clang-format off
1576         std::vector<LineBreakExpectation> expect = {
1577                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1578                 {"\u6674\u5929\u306A\u308A", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1579         };
1580         // clang-format on
1581         const auto actual = doLineBreak(LINE_WIDTH);
1582         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1583                                                    << " vs " << std::endl
1584                                                    << toString(textBuf, actual);
1585     }
1586     {
1587         constexpr float LINE_WIDTH = 60;
1588         // "\u6674\u5929" is a single replacement span. Do not break.
1589         // clang-format off
1590         std::vector<LineBreakExpectation> expect = {
1591                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1592                 {"\u6674\u5929\u306A", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1593                 {"\u308A",             10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1594         };
1595         // clang-format on
1596         const auto actual = doLineBreak(LINE_WIDTH);
1597         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1598                                                    << " vs " << std::endl
1599                                                    << toString(textBuf, actual);
1600     }
1601     {
1602         constexpr float LINE_WIDTH = 50;
1603         // "\u6674\u5929" is a single replacement span. Do not break.
1604         // clang-format off
1605         std::vector<LineBreakExpectation> expect = {
1606                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1607                 {"\u6674\u5929",       50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1608                 {"\u306A\u308A",       20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1609         };
1610         // clang-format on
1611         const auto actual = doLineBreak(LINE_WIDTH);
1612         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1613                                                    << " vs " << std::endl
1614                                                    << toString(textBuf, actual);
1615     }
1616     {
1617         constexpr float LINE_WIDTH = 40;
1618         // "\u6674\u5929" is a single replacement span. Do not break.
1619         // clang-format off
1620         std::vector<LineBreakExpectation> expect = {
1621                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1622                 {"\u6674\u5929",       50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1623                 {"\u306A\u308A",       20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1624         };
1625         // clang-format on
1626         const auto actual = doLineBreak(LINE_WIDTH);
1627         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1628                                                    << " vs " << std::endl
1629                                                    << toString(textBuf, actual);
1630     }
1631     {
1632         constexpr float LINE_WIDTH = 10;
1633         // "\u6674\u5929" is a single replacement span. Do not break.
1634         // clang-format off
1635         std::vector<LineBreakExpectation> expect = {
1636                 {"\u672C",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1637                 {"\u65E5",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1638                 {"\u306F",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1639                 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1640                 {"\u306A",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1641                 {"\u308A",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1642         };
1643         // clang-format on
1644         const auto actual = doLineBreak(LINE_WIDTH);
1645         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1646                                                    << " vs " << std::endl
1647                                                    << toString(textBuf, actual);
1648     }
1649 }
1650 
1651 // http://b/119657685
1652 // Following test case is for verifying that the ReplacementSpan should not be broken into multiple
1653 // pieces. The actual break point is not a part of expectation. For example, it would be good to
1654 // break the starting offset of the ReplacementSpan for some case.
TEST_F(OptimalLineBreakerTest,testReplacementSpan_GraphemeLineBreakWithMultipleRepalcementSpans)1655 TEST_F(OptimalLineBreakerTest, testReplacementSpan_GraphemeLineBreakWithMultipleRepalcementSpans) {
1656     constexpr float CHAR_WIDTH = 10.0;
1657 
1658     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1659     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1660 
1661     const auto textBuf = utf8ToUtf16("ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0pq st");
1662 
1663     auto doLineBreak = [=](float width) {
1664         MeasuredTextBuilder builder;
1665         builder.addReplacementRun(0, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1666         builder.addCustomRun<ConstantRun>(Range(5, 7), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1667         builder.addReplacementRun(7, 12, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1668         builder.addCustomRun<ConstantRun>(Range(12, 14), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1669         builder.addReplacementRun(14, 19, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1670         builder.addCustomRun<ConstantRun>(Range(19, 21), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1671         builder.addReplacementRun(21, 26, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1672 
1673         std::unique_ptr<MeasuredText> measuredText =
1674                 builder.build(textBuf, false /* compute hyphenation */,
1675                               false /* compute full layout */, nullptr /* no hint */);
1676         RectangleLineWidth rectangleLineWidth(width);
1677         TabStops tabStops(nullptr, 0, 0);
1678         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1679                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1680                                 false /* justified */);
1681     };
1682 
1683     {
1684         constexpr float LINE_WIDTH = 1000;
1685         // clang-format off
1686         std::vector<LineBreakExpectation> expect = {
1687                 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0pq st",
1688                   260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1689         };
1690         // clang-format on
1691         const auto actual = doLineBreak(LINE_WIDTH);
1692         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1693                                                    << " vs " << std::endl
1694                                                    << toString(textBuf, actual);
1695     }
1696     {
1697         constexpr float LINE_WIDTH = 250;
1698         // clang-format off
1699         std::vector<LineBreakExpectation> expect = {
1700                 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0",
1701                   210, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1702                 {"pq st",
1703                    50, NO_START_HYPHEN, NO_END_HYPHEN,       0,      0},
1704         };
1705         // clang-format on
1706         const auto actual = doLineBreak(LINE_WIDTH);
1707         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1708                                                    << " vs " << std::endl
1709                                                    << toString(textBuf, actual);
1710     }
1711     {
1712         constexpr float LINE_WIDTH = 180;
1713         // clang-format off
1714         std::vector<LineBreakExpectation> expect = {
1715                 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0",
1716                   140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1717                 {"kl no\u00A0\u00A0pq st",
1718                   120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1719         };
1720         // clang-format on
1721         const auto actual = doLineBreak(LINE_WIDTH);
1722         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1723                                                    << " vs " << std::endl
1724                                                    << toString(textBuf, actual);
1725     }
1726     {
1727         constexpr float LINE_WIDTH = 130;
1728         // clang-format off
1729         std::vector<LineBreakExpectation> expect = {
1730                 {"ab de\u00A0\u00A0fg ij\u00A0",
1731                   130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1732                 {"\u00A0kl no\u00A0\u00A0pq st",
1733                   130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1734         };
1735         // clang-format on
1736         const auto actual = doLineBreak(LINE_WIDTH);
1737         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1738                                                    << " vs " << std::endl
1739                                                    << toString(textBuf, actual);
1740     }
1741     {
1742         constexpr float LINE_WIDTH = 110;
1743         // clang-format off
1744         std::vector<LineBreakExpectation> expect = {
1745                 {"ab de\u00A0",             60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1746                 {"\u00A0fg ij\u00A0\u00A0", 80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1747                 {"kl no\u00A0\u00A0",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1748                 {"pq st",                   50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1749         };
1750         // clang-format on
1751         const auto actual = doLineBreak(LINE_WIDTH);
1752         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1753                                                    << " vs " << std::endl
1754                                                    << toString(textBuf, actual);
1755     }
1756     {
1757         constexpr float LINE_WIDTH = 60;
1758         // clang-format off
1759         std::vector<LineBreakExpectation> expect = {
1760                 {"ab de\u00A0",  60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1761                 {"\u00A0fg ij",  60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1762                 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1763                 {"kl no\u00A0",  60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1764                 {"\u00A0pq st",  60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1765         };
1766         // clang-format on
1767         const auto actual = doLineBreak(LINE_WIDTH);
1768         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1769                                                    << " vs " << std::endl
1770                                                    << toString(textBuf, actual);
1771     }
1772     {
1773         constexpr float LINE_WIDTH = 50;
1774         // clang-format off
1775         std::vector<LineBreakExpectation> expect = {
1776                 {"ab de",        50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1777                 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1778                 {"fg ij",        50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1779                 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1780                 {"kl no",        50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1781                 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1782                 {"pq st",        50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1783         };
1784         // clang-format on
1785         const auto actual = doLineBreak(LINE_WIDTH);
1786         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1787                                                    << " vs " << std::endl
1788                                                    << toString(textBuf, actual);
1789     }
1790 }
1791 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_with_punctuation)1792 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation) {
1793     constexpr float CHAR_WIDTH = 10.0;
1794 
1795     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1796     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1797 
1798     const auto textBuf = utf8ToUtf16("This (is an) example text.");
1799 
1800     // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1801     auto doLineBreak = [=](float width) {
1802         MeasuredTextBuilder builder;
1803         builder.addCustomRun<ConstantRun>(Range(0, 6), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1804         builder.addReplacementRun(6, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1805         builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1806                                           DESCENT);
1807 
1808         std::unique_ptr<MeasuredText> measuredText =
1809                 builder.build(textBuf, false /* compute hyphenation */,
1810                               false /* compute full layout */, nullptr /* no hint */);
1811         RectangleLineWidth rectangleLineWidth(width);
1812         TabStops tabStops(nullptr, 0, 0);
1813         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1814                                 BreakStrategy::HighQuality, HyphenationFrequency::Normal,
1815                                 false /* justified */);
1816     };
1817 
1818     {
1819         constexpr float LINE_WIDTH = 1000;
1820         // "is an" is a single replacement span. Do not break.
1821         // clang-format off
1822         std::vector<LineBreakExpectation> expect = {
1823                 {"This (is an) example text.",
1824                   260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1825         };
1826         // clang-format on
1827         const auto actual = doLineBreak(LINE_WIDTH);
1828         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1829                                                    << " vs " << std::endl
1830                                                    << toString(textBuf, actual);
1831     }
1832     {
1833         constexpr float LINE_WIDTH = 250;
1834         // "is an" is a single replacement span. Do not break.
1835         // clang-format off
1836         std::vector<LineBreakExpectation> expect = {
1837                 {"This (is an) example ", 200, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1838                 {"text.",                  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1839         };
1840         // clang-format on
1841         const auto actual = doLineBreak(LINE_WIDTH);
1842         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1843                                                    << " vs " << std::endl
1844                                                    << toString(textBuf, actual);
1845     }
1846     {
1847         constexpr float LINE_WIDTH = 190;
1848         // "is an" is a single replacement span. Do not break.
1849         // clang-format off
1850         std::vector<LineBreakExpectation> expect = {
1851                 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1852                 {"example text.", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1853         };
1854         // clang-format on
1855         const auto actual = doLineBreak(LINE_WIDTH);
1856         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1857                                                    << " vs " << std::endl
1858                                                    << toString(textBuf, actual);
1859     }
1860     {
1861         constexpr float LINE_WIDTH = 120;
1862         // "is an" is a single replacement span. Do not break.
1863         // clang-format off
1864         std::vector<LineBreakExpectation> expect = {
1865                 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1866                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1867                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1868         };
1869         // clang-format on
1870         const auto actual = doLineBreak(LINE_WIDTH);
1871         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1872                                                    << " vs " << std::endl
1873                                                    << toString(textBuf, actual);
1874     }
1875     {
1876         constexpr float LINE_WIDTH = 110;
1877         // "is an" is a single replacement span. Do not break.
1878         // clang-format off
1879         std::vector<LineBreakExpectation> expect = {
1880                 {"This ",    40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1881                 {"(is an) ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1882                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1883                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1884         };
1885         // clang-format on
1886         const auto actual = doLineBreak(LINE_WIDTH);
1887         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1888                                                    << " vs " << std::endl
1889                                                    << toString(textBuf, actual);
1890     }
1891     {
1892         constexpr float LINE_WIDTH = 60;
1893         // "is an" is a single replacement span. Do not break.
1894         // clang-format off
1895         std::vector<LineBreakExpectation> expect = {
1896                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1897                 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1898                 {") ex",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1899                 {"ample ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1900                 {"text.",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1901         };
1902         // clang-format on
1903         const auto actual = doLineBreak(LINE_WIDTH);
1904         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1905                                                    << " vs " << std::endl
1906                                                    << toString(textBuf, actual);
1907     }
1908     {
1909         constexpr float LINE_WIDTH = 50;
1910         // "is an" is a single replacement span. Do not break.
1911         // clang-format off
1912         std::vector<LineBreakExpectation> expect = {
1913                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1914                 {"(",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1915                 {"is an",  50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1916                 {") ex",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1917                 {"ample ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1918                 {"text.",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1919         };
1920         // clang-format on
1921         const auto actual = doLineBreak(LINE_WIDTH);
1922         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1923                                                    << " vs " << std::endl
1924                                                    << toString(textBuf, actual);
1925     }
1926     {
1927         constexpr float LINE_WIDTH = 40;
1928         // "is an" is a single replacement span. Do not break.
1929         // clang-format off
1930         std::vector<LineBreakExpectation> expect = {
1931                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1932                 // TODO(nona): This might be wrongly broken. "(is an" should be broken into "(" and
1933                 // "is an" as the desperate break.
1934                 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1935                 {") ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1936                 {"exa",    30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1937                 {"mple ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1938                 {"text",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1939                 {".",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1940         };
1941         // clang-format on
1942         const auto actual = doLineBreak(LINE_WIDTH);
1943         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1944                                                    << " vs " << std::endl
1945                                                    << toString(textBuf, actual);
1946     }
1947     {
1948         constexpr float LINE_WIDTH = 10;
1949         // "is an" is a single replacement span. Do not break.
1950         // clang-format off
1951         std::vector<LineBreakExpectation> expect = {
1952                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1953                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1954                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1955                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1956                 // TODO(nona): This might be wrongly broken. "(is an" should be broken into "(" and
1957                 // "is an" as the desperate break.
1958                 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1959                 {") ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1960                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1961                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1962                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1963                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1964                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1965                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1966                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1967                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1968                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1969                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1970                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1971                 {".",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1972         };
1973         // clang-format on
1974         const auto actual = doLineBreak(LINE_WIDTH);
1975         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1976                                                    << " vs " << std::endl
1977                                                    << toString(textBuf, actual);
1978     }
1979 }
1980 
TEST_F(OptimalLineBreakerTest,testControllCharAfterSpace)1981 TEST_F(OptimalLineBreakerTest, testControllCharAfterSpace) {
1982     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1983     constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
1984     constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
1985     const std::vector<uint16_t> textBuf = utf8ToUtf16("example \u2066example");
1986 
1987     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1988     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1989     {
1990         constexpr float LINE_WIDTH = 90;
1991         // Note that HarfBuzz assigns 0px for control characters regardless of glyph existence in
1992         // the font.
1993         std::vector<LineBreakExpectation> expect = {
1994                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1995                 {"\u2066example", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1996         };
1997 
1998         auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1999         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2000                                                    << " vs " << std::endl
2001                                                    << toString(textBuf, actual);
2002         actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
2003         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2004                                                    << " vs " << std::endl
2005                                                    << toString(textBuf, actual);
2006     }
2007 }
2008 }  // namespace
2009 }  // namespace minikin
2010