xref: /aosp_15_r20/frameworks/minikin/tests/unittest/OptimalLineBreakerTest.cpp (revision 834a2baab5fdfc28e9a428ee87c7ea8f6a06a53d)
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 <com_android_text_flags.h>
18 #include <flag_macros.h>
19 #include <gtest/gtest.h>
20 
21 #include <memory>
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 #include "minikin/Hyphenator.h"
33 
34 namespace minikin {
35 namespace {
36 
37 using line_breaker_test_helper::ConstantRun;
38 using line_breaker_test_helper::LineBreakExpectation;
39 using line_breaker_test_helper::RectangleLineWidth;
40 using line_breaker_test_helper::sameLineBreak;
41 using line_breaker_test_helper::toString;
42 
43 // The ascent/descent of Ascii.ttf with text size = 10.
44 constexpr float ASCENT = -80.0f;
45 constexpr float DESCENT = 20.0f;
46 
47 // The ascent/descent of CustomExtent.ttf with text size = 10.
48 constexpr float CUSTOM_ASCENT = -160.0f;
49 constexpr float CUSTOM_DESCENT = 40.0f;
50 
51 // A test string for Japanese. The meaning is that "Today is a sunny day."
52 // The expected line break of phrase and non-phrase cases are:
53 //     Phrase: | \u672C\u65E5\u306F | \u6674\u5929\u306A\u308A\u3002 |
54 // Non-Phrase: | \u672C | \u65E5 | \u306F | \u6674 | \u5929 | \u306A | \u308A\u3002 |
55 const char* JP_TEXT = "\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002";
56 
57 class OptimalLineBreakerTest : public testing::Test {
58 public:
OptimalLineBreakerTest()59     OptimalLineBreakerTest() {}
60 
~OptimalLineBreakerTest()61     virtual ~OptimalLineBreakerTest() {}
62 
SetUp()63     virtual void SetUp() override {
64         mHyphenationPattern = readWholeFile("/system/usr/hyphen-data/hyph-en-us.hyb");
65         Hyphenator* hyphenator =
66                 Hyphenator::loadBinary(mHyphenationPattern.data(), mHyphenationPattern.size(),
67                                        2 /* min prefix */, 2 /* min suffix */, "en-US");
68         HyphenatorMap::add("en-US", hyphenator);
69         HyphenatorMap::add("pl", Hyphenator::loadBinary(nullptr, 0, 0, 0, "pl"));
70     }
71 
TearDown()72     virtual void TearDown() override { HyphenatorMap::clear(); }
73 
74 protected:
doLineBreak(const U16StringPiece & textBuffer,BreakStrategy strategy,HyphenationFrequency frequency,const std::string & lang,float lineWidth,bool ignoreKerning)75     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, BreakStrategy strategy,
76                                 HyphenationFrequency frequency, const std::string& lang,
77                                 float lineWidth, bool ignoreKerning) {
78         MeasuredTextBuilder builder;
79         auto family1 = buildFontFamily("Ascii.ttf");
80         auto family2 = buildFontFamily("CustomExtent.ttf");
81         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
82         auto fc = FontCollection::create(families);
83         MinikinPaint paint(fc);
84         paint.size = 10.0f;  // Make 1em=10px
85         paint.localeListId = LocaleListCache::getId(lang);
86         builder.addStyleRun(0, textBuffer.size(), std::move(paint), 0, 0, true, false);
87         bool computeHyphen = frequency != HyphenationFrequency::None;
88         std::unique_ptr<MeasuredText> measuredText =
89                 builder.build(textBuffer, computeHyphen, false /* compute full layout */,
90                               false /* computeBounds */, ignoreKerning, nullptr /* no hint */);
91         return doLineBreak(textBuffer, *measuredText, strategy, frequency, lineWidth);
92     }
93 
doLineBreakForJapanese(const U16StringPiece & textBuffer,LineBreakWordStyle lbwStyle,const std::string & lang,float lineWidth)94     LineBreakResult doLineBreakForJapanese(const U16StringPiece& textBuffer,
95                                            LineBreakWordStyle lbwStyle, const std::string& lang,
96                                            float lineWidth) {
97         MeasuredTextBuilder builder;
98         auto family1 = buildFontFamily("Japanese.ttf");
99         std::vector<std::shared_ptr<FontFamily>> families = {family1};
100         auto fc = FontCollection::create(families);
101         MinikinPaint paint(fc);
102         paint.size = 10.0f;  // Make 1em=10px
103         paint.localeListId = LocaleListCache::getId(lang);
104         builder.addStyleRun(0, textBuffer.size(), std::move(paint), 0, (int)lbwStyle, true, false);
105         std::unique_ptr<MeasuredText> measuredText = builder.build(
106                 textBuffer, false /* computeHyphen */, false /* compute full layout */,
107                 false /* computeBounds */, false /* ignoreKerning */, nullptr /* no hint */);
108         return doLineBreak(textBuffer, *measuredText, BreakStrategy::HighQuality,
109                            HyphenationFrequency::None, lineWidth);
110     }
doLineBreakWithNoHyphenSpan(const U16StringPiece & textBuffer,const Range & noHyphenRange,float lineWidth)111     LineBreakResult doLineBreakWithNoHyphenSpan(const U16StringPiece& textBuffer,
112                                                 const Range& noHyphenRange, float lineWidth) {
113         MeasuredTextBuilder builder;
114         auto family1 = buildFontFamily("Ascii.ttf");
115         auto family2 = buildFontFamily("CustomExtent.ttf");
116         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
117         auto fc = FontCollection::create(families);
118         if (noHyphenRange.getStart() != 0) {
119             MinikinPaint paint(fc);
120             paint.size = 10.0f;  // Make 1em=10px
121             paint.localeListId = LocaleListCache::getId("en-US");
122             builder.addStyleRun(0, noHyphenRange.getStart(), std::move(paint), 0, 0,
123                                 true /* hyphenation */, false);
124         }
125         MinikinPaint paint(fc);
126         paint.size = 10.0f;  // Make 1em=10px
127         paint.localeListId = LocaleListCache::getId("en-US");
128         builder.addStyleRun(noHyphenRange.getStart(), noHyphenRange.getEnd(), std::move(paint), 0,
129                             0, false /* no hyphenation */, false);
130         if (noHyphenRange.getEnd() != textBuffer.size()) {
131             MinikinPaint paint(fc);
132             paint.size = 10.0f;  // Make 1em=10px
133             paint.localeListId = LocaleListCache::getId("en-US");
134             builder.addStyleRun(noHyphenRange.getEnd(), textBuffer.size(), std::move(paint), 0, 0,
135                                 true /* hyphenation */, false);
136         }
137         std::unique_ptr<MeasuredText> measuredText = builder.build(
138                 textBuffer, true /* computeHyphen */, false /* compute full layout */,
139                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
140         return doLineBreak(textBuffer, *measuredText, BreakStrategy::HighQuality,
141                            HyphenationFrequency::Normal, lineWidth);
142     }
143 
doLineBreakForBounds(const U16StringPiece & textBuffer,BreakStrategy strategy,HyphenationFrequency frequency,float lineWidth)144     LineBreakResult doLineBreakForBounds(const U16StringPiece& textBuffer, BreakStrategy strategy,
145                                          HyphenationFrequency frequency, float lineWidth) {
146         MeasuredTextBuilder builder;
147         auto family1 = buildFontFamily("OvershootTest.ttf");
148         auto family2 = buildFontFamily("Ascii.ttf");
149         std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
150         auto fc = FontCollection::create(families);
151         MinikinPaint paint(fc);
152         paint.size = 10.0f;  // Make 1em=10px
153         paint.localeListId = LocaleListCache::getId("en-US");
154         builder.addStyleRun(0, textBuffer.size(), std::move(paint), 0, 0, true, false);
155         bool computeHyphen = frequency != HyphenationFrequency::None;
156         std::unique_ptr<MeasuredText> measuredText = builder.build(
157                 textBuffer, computeHyphen, false /* compute full layout */,
158                 true /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
159         RectangleLineWidth rectangleLineWidth(lineWidth);
160         return breakLineOptimal(textBuffer, *measuredText, rectangleLineWidth, strategy, frequency,
161                                 false /* justified */, true /* useBoundsForWidth */);
162     }
163 
doLineBreak(const U16StringPiece & textBuffer,const MeasuredText & measuredText,BreakStrategy strategy,HyphenationFrequency frequency,float lineWidth)164     LineBreakResult doLineBreak(const U16StringPiece& textBuffer, const MeasuredText& measuredText,
165                                 BreakStrategy strategy, HyphenationFrequency frequency,
166                                 float lineWidth) {
167         RectangleLineWidth rectangleLineWidth(lineWidth);
168         return breakLineOptimal(textBuffer, measuredText, rectangleLineWidth, strategy, frequency,
169                                 false /* justified */, false /* useBoundsForWidth */);
170     }
171 
expectBreak(const std::vector<LineBreakExpectation> & expect,const U16StringPiece & textBuffer,BreakStrategy strategy,HyphenationFrequency frequency,const std::string & lang,float lineWidth)172     void expectBreak(const std::vector<LineBreakExpectation>& expect,
173                      const U16StringPiece& textBuffer, BreakStrategy strategy,
174                      HyphenationFrequency frequency, const std::string& lang, float lineWidth) {
175         {
176             char msg[256] = {};
177             snprintf(msg, 256, "width = %f, lang = %s, strategy = %u, frequency = %u, fullyHyphen",
178                      lineWidth, lang.c_str(), (uint32_t)strategy, (uint32_t)frequency);
179             SCOPED_TRACE(msg);
180             auto actual = doLineBreak(textBuffer, strategy, frequency, lang, lineWidth,
181                                       false /* ignoreKerning */);
182             EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
183                                                        << " vs " << std::endl
184                                                        << toString(textBuffer, actual);
185         }
186         {
187             char msg[256] = {};
188             snprintf(msg, 256, "width = %f, lang = %s, strategy = %u, frequency = %u, fullyHyphen",
189                      lineWidth, lang.c_str(), (uint32_t)strategy, (uint32_t)frequency);
190             SCOPED_TRACE(msg);
191             auto actual = doLineBreak(textBuffer, strategy, frequency, lang, lineWidth,
192                                       true /* ignoreKerning */);
193             EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
194                                                        << " vs " << std::endl
195                                                        << toString(textBuffer, actual);
196         }
197     }
198 
doLineBreakWithLetterSpacing(const U16StringPiece & textBuffer,BreakStrategy strategy,HyphenationFrequency frequency,float letterSpacing,float lineWidth)199     LineBreakResult doLineBreakWithLetterSpacing(const U16StringPiece& textBuffer,
200                                                  BreakStrategy strategy,
201                                                  HyphenationFrequency frequency,
202                                                  float letterSpacing, float lineWidth) {
203         MeasuredTextBuilder builder;
204         auto family1 = buildFontFamily("Ascii.ttf");
205         std::vector<std::shared_ptr<FontFamily>> families = {family1};
206         auto fc = FontCollection::create(families);
207         MinikinPaint paint(fc);
208         paint.size = 10.0f;  // Make 1em=10px
209         paint.scaleX = 1.0f;
210         paint.letterSpacing = letterSpacing;
211         paint.localeListId = LocaleListCache::getId("en-US");
212         builder.addStyleRun(0, textBuffer.size(), std::move(paint), 0, 0, true, false);
213         bool computeHyphen = frequency != HyphenationFrequency::None;
214         std::unique_ptr<MeasuredText> measuredText = builder.build(
215                 textBuffer, computeHyphen, false /* compute full layout */,
216                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
217         return doLineBreak(textBuffer, *measuredText, strategy, frequency, lineWidth);
218     }
219 
220 private:
221     std::vector<uint8_t> mHyphenationPattern;
222 };
223 
TEST_F(OptimalLineBreakerTest,testBreakWithoutHyphenation)224 TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
225     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
226     constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
227     constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
228     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
229     const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
230 
231     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
232     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
233     constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
234     // Note that disable clang-format everywhere since aligned expectation is more readable.
235     {
236         constexpr float LINE_WIDTH = 1000;
237         std::vector<LineBreakExpectation> expect = {
238                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
239         };
240 
241         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
242         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
243         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
244         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
245     }
246     {
247         constexpr float LINE_WIDTH = 240;
248         std::vector<LineBreakExpectation> expect = {
249                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
250         };
251         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
252         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
253         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
254         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
255     }
256     {
257         constexpr float LINE_WIDTH = 230;
258         // clang-format off
259         std::vector<LineBreakExpectation> expect = {
260                 { "This is an example " , 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
261                 { "text."               ,  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
262         };
263         // clang-format on
264 
265         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
266         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
267 
268         // clang-format off
269         expect = {
270                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
271                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
272         };
273         // clang-format on
274         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
275 
276         // clang-format off
277         expect = {
278                 { "This is an ex-" , 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
279                 { "ample text."    , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
280         };
281         // clang-format on
282         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
283     }
284     return;
285     {
286         constexpr float LINE_WIDTH = 170;
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         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
295 
296         // clang-format off
297         expect = {
298                 { "This is an exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
299                 { "ple text."        ,  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
300         };
301         // clang-format on
302         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
303 
304         // clang-format off
305         expect = {
306                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
307                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
308         };
309         // clang-format on
310         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
311 
312         // clang-format off
313         expect = {
314                 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
315                 { "ample text."   , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
316         };
317         // clang-format on
318         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
319     }
320     {
321         constexpr float LINE_WIDTH = 160;
322         // clang-format off
323         std::vector<LineBreakExpectation> expect = {
324                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
325                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
326         };
327         // clang-format on
328         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
329 
330         // clang-format off
331         expect = {
332                 { "This is an exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
333                 { "ple text."        ,  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
334         };
335         // clang-format on
336         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
337 
338         // clang-format off
339         expect = {
340                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
341                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
342         };
343         // clang-format on
344         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
345 
346         // clang-format off
347         expect = {
348                 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
349                 { "ample text."   , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
350         };
351         // clang-format on
352         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
353     }
354     {
355         constexpr float LINE_WIDTH = 150;
356         // clang-format off
357         std::vector<LineBreakExpectation> expect = {
358                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
359                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
360         };
361         // clang-format on
362         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
363 
364         // clang-format off
365         expect = {
366                 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
367                 { "ample text."   , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
368         };
369         // clang-format on
370         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
371 
372         // clang-format off
373         expect = {
374                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
375                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
376         };
377         // clang-format on
378         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
379 
380         // clang-format off
381         expect = {
382                 { "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
383                 { "ample text."   , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
384         };
385         // clang-format on
386         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
387     }
388     {
389         constexpr float LINE_WIDTH = 130;
390         // clang-format off
391         std::vector<LineBreakExpectation> expect = {
392                 { "This is an "   , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
393                 { "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
394         };
395         // clang-format on
396 
397         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
398         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
399         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
400         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
401     }
402     {
403         constexpr float LINE_WIDTH = 120;
404         // clang-format off
405         std::vector<LineBreakExpectation> expect = {
406                 { "This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
407                 { "example "   ,  70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
408                 { "text."      ,  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
409         };
410         // clang-format on
411 
412         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
413         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
414         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
415 
416         // clang-format off
417         expect = {
418                 { "This is " ,  70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
419                 { "an exam-" ,  80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
420                 { "ple text.",  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
421         };
422         // clang-format on
423 
424         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
425     }
426     {
427         constexpr float LINE_WIDTH = 90;
428         // clang-format off
429         std::vector<LineBreakExpectation> expect = {
430                 { "This "   , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
431                 { "is an "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
432                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
433                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
434         };
435         // clang-format on
436         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
437 
438         // clang-format off
439         expect = {
440                 { "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
441                 { "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
442                 { "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
443         };
444         // clang-format on
445         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
446 
447         // clang-format off
448         expect = {
449                 { "This "   , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
450                 { "is an "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
451                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
452                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
453         };
454         // clang-format on
455         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
456 
457         // clang-format off
458         expect = {
459                 { "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
460                 { "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
461                 { "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
462         };
463         // clang-format on
464         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
465     }
466     {
467         constexpr float LINE_WIDTH = 80;
468         // clang-format off
469         std::vector<LineBreakExpectation> expect = {
470                 { "This "   , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
471                 { "is an "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
472                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
473                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
474         };
475         // clang-format on
476 
477         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
478         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
479 
480         // clang-format off
481         expect = {
482                 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
483                 { "an ex-"  , 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
484                 { "ample "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
485                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
486         };
487         // clang-format on
488         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
489         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
490     }
491     {
492         constexpr float LINE_WIDTH = 70;
493         // clang-format off
494         std::vector<LineBreakExpectation> expect = {
495                 { "This "   , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
496                 { "is an "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
497                 { "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
498                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
499         };
500         // clang-format on
501 
502         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
503         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
504         // clang-format off
505         expect = {
506                 { "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
507                 { "an ex-"  , 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
508                 { "ample "  , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
509                 { "text."   , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
510         };
511         // clang-format on
512         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
513         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
514     }
515     {
516         constexpr float LINE_WIDTH = 60;
517         // clang-format off
518         std::vector<LineBreakExpectation> expect = {
519                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
520                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
521                 // TODO: Is this desperate break working correctly?
522                 { "exa"   , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
523                 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
524                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
525         };
526         // clang-format on
527 
528         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
529         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
530         // clang-format off
531         expect = {
532                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
533                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
534                 { "exam-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
535                 { "ple "  , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
536                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
537         };
538         // clang-format on
539         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
540         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
541     }
542     {
543         constexpr float LINE_WIDTH = 50;
544         // clang-format off
545         std::vector<LineBreakExpectation> expect = {
546                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
547                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
548                 // TODO: Is this desperate break working correctly?
549                 { "exa"   , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
550                 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
551                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
552         };
553         // clang-format on
554 
555         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
556         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
557         // clang-format off
558         expect = {
559                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
560                 { "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
561                 { "exam-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
562                 { "ple "  , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
563                 { "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
564         };
565         // clang-format on
566         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
567         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
568     }
569     {
570         constexpr float LINE_WIDTH = 40;
571         // clang-format off
572         std::vector<LineBreakExpectation> expect = {
573                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
574                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
575                 { "an "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
576                 // TODO: Is this desperate break working correctly?
577                 { "exa"   , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
578                 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
579                 { "text"  , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
580                 { "."     , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
581         };
582         // clang-format on
583         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
584 
585         // clang-format off
586         expect = {
587                 { "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
588                 { "is "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
589                 { "an "   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
590                 // TODO: Is this desperate break working correctly?
591                 { "exa"   , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
592                 { "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
593                 // TODO: Is this desperate break working correctly?
594                 { "t"     , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
595                 { "ext."  , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
596         };
597         // clang-format on
598         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
599 
600         // clang-format off
601         expect = {
602                 { "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
603                 { "is "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
604                 { "an "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
605                 { "ex-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
606                 { "am-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
607                 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
608                 { "text" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
609                 { "."    , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
610         };
611         // clang-format on
612         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
613         // clang-format off
614         expect = {
615                 { "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
616                 { "is "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
617                 { "an "  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
618                 { "ex-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
619                 { "am-"  , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
620                 { "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
621                 // TODO: Is this desperate break working correctly?
622                 { "te"   , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
623                 { "xt."  , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
624         };
625         // clang-format on
626         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
627     }
628     {
629         constexpr float LINE_WIDTH = 30;
630         // clang-format off
631         std::vector<LineBreakExpectation> expect = {
632                 // TODO: Is this desperate break working correctly?
633                 { "T"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
634                 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
635                 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
636                 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
637                 // TODO: Is this desperate break working correctly?
638                 { "e"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
639                 { "xam" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
640                 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
641                 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
642                 { "t."  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
643         };
644         // clang-format on
645         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
646 
647         // clang-format off
648         expect = {
649                 // TODO: Is this desperate break working correctly?
650                 { "T"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
651                 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
652                 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
653                 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
654                 // TODO: Is this desperate break working correctly?
655                 { "e"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
656                 { "xam" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
657                 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
658                 // TODO: Is this desperate break working correctly?
659                 { "te"  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
660                 { "xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
661         };
662         // clang-format on
663         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
664 
665         // clang-format off
666         expect = {
667                 // TODO: Is this desperate break working correctly?
668                 { "T"   , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
669                 { "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
670                 { "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
671                 { "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
672                 { "ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
673                 { "am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
674                 { "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
675                 { "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
676                 { "t."  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
677         };
678         // clang-format on
679         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
680 
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                 {"ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
689                 {"am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
690                 {"ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
691                 // TODO: Is this desperate break working correctly?
692                 {"te"  , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
693                 {"xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
694         };
695         // clang-format on
696         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
697     }
698     {
699         constexpr float LINE_WIDTH = 20;
700         // clang-format off
701         std::vector<LineBreakExpectation> expect = {
702                 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
703                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
704                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
705                 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
706                 // TODO: Is this desperate break working correctly?
707                 { "e"  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
708                 { "xa" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
709                 { "mp" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
710                 { "le ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
711                 { "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
712                 { "xt" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
713                 { "."  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
714         };
715         // clang-format on
716         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
717         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
718 
719         // clang-format off
720         expect = {
721                 { "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
722                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
723                 { "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
724                 { "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
725                 // TODO: Is this desperate break working correctly?
726                 { "e"  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
727                 { "xa" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
728                 { "mp" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
729                 { "le ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
730                 // TODO: Is this desperate break working correctly?
731                 { "t"  , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
732                 { "ex" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
733                 { "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
734         };
735         // clang-format on
736         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
737         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
738     }
739     {
740         constexpr float LINE_WIDTH = 10;
741         // clang-format off
742         std::vector<LineBreakExpectation> expect = {
743                 { "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
744                 { "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
745                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
746                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
747                 { "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
748                 { "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
749                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
750                 { "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
751                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
752                 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
753                 { "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
754                 { "m" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
755                 { "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
756                 { "l" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
757                 { "e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
758                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
759                 { "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
760                 { "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
761                 { "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
762                 { "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
763         };
764         // clang-format on
765 
766         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
767         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
768         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
769         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
770     }
771 }
772 
TEST_F(OptimalLineBreakerTest,testHyphenationStartLineChange)773 TEST_F(OptimalLineBreakerTest, testHyphenationStartLineChange) {
774     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
775     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
776     // "hyphenation" is hyphnated to "hy-phen-a-tion".
777     const std::vector<uint16_t> textBuf = utf8ToUtf16("czerwono-niebieska");
778 
779     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
780     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
781     constexpr StartHyphenEdit START_HYPHEN = StartHyphenEdit::INSERT_HYPHEN;
782 
783     // Note that disable clang-format everywhere since aligned expectation is more readable.
784     {
785         constexpr float LINE_WIDTH = 1000;
786         std::vector<LineBreakExpectation> expect = {
787                 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
788         };
789 
790         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
791     }
792     {
793         constexpr float LINE_WIDTH = 180;
794         std::vector<LineBreakExpectation> expect = {
795                 {"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
796         };
797 
798         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
799     }
800     {
801         constexpr float LINE_WIDTH = 130;
802         // clang-format off
803         std::vector<LineBreakExpectation> expect = {
804                 {"czerwono-" ,  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
805                 {"-niebieska", 100,    START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
806         };
807         // clang-format on
808 
809         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
810     }
811 }
812 
TEST_F(OptimalLineBreakerTest,testZeroWidthLine)813 TEST_F(OptimalLineBreakerTest, testZeroWidthLine) {
814     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
815     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
816     constexpr float LINE_WIDTH = 0;
817 
818     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
819     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
820 
821     {
822         const auto textBuf = utf8ToUtf16("");
823         std::vector<LineBreakExpectation> expect = {};
824         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
825     }
826     {
827         const auto textBuf = utf8ToUtf16("A");
828         std::vector<LineBreakExpectation> expect = {
829                 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
830         };
831         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
832     }
833     {
834         const auto textBuf = utf8ToUtf16("AB");
835         std::vector<LineBreakExpectation> expect = {
836                 {"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
837                 {"B", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
838         };
839         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
840     }
841 }
842 
TEST_F(OptimalLineBreakerTest,testZeroWidthCharacter)843 TEST_F(OptimalLineBreakerTest, testZeroWidthCharacter) {
844     constexpr float CHAR_WIDTH = 0.0;
845     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
846     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
847 
848     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
849     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
850     {
851         constexpr float LINE_WIDTH = 1.0;
852         const auto textBuf = utf8ToUtf16("This is an example text.");
853         std::vector<LineBreakExpectation> expect = {
854                 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
855         };
856         MeasuredTextBuilder builder;
857         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
858                                           DESCENT);
859         std::unique_ptr<MeasuredText> measuredText = builder.build(
860                 textBuf, true /* compute hyphenation */, false /* compute full layout */,
861                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
862 
863         const auto actual =
864                 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
865         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
866                                                    << " vs " << std::endl
867                                                    << toString(textBuf, actual);
868     }
869     {
870         constexpr float LINE_WIDTH = 0.0;
871         const auto textBuf = utf8ToUtf16("This is an example text.");
872         std::vector<LineBreakExpectation> expect = {
873                 {"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
874         };
875         MeasuredTextBuilder builder;
876         builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
877                                           DESCENT);
878         std::unique_ptr<MeasuredText> measuredText = builder.build(
879                 textBuf, true /* compute hyphenation */, false /* compute full layout */,
880                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
881 
882         const auto actual =
883                 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, 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,testLocaleSwitchTest)890 TEST_F(OptimalLineBreakerTest, testLocaleSwitchTest) {
891     constexpr float CHAR_WIDTH = 10.0;
892     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
893     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
894 
895     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
896     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
897 
898     constexpr float LINE_WIDTH = 240;
899     const auto textBuf = utf8ToUtf16("This is an example text.");
900     {
901         std::vector<LineBreakExpectation> expect = {
902                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
903         };
904 
905         MeasuredTextBuilder builder;
906         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
907         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
908                                           DESCENT);
909         std::unique_ptr<MeasuredText> measuredText = builder.build(
910                 textBuf, true /* compute hyphenation */, false /* compute full layout */,
911                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
912 
913         const auto actual =
914                 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
915         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
916                                                    << " vs " << std::endl
917                                                    << toString(textBuf, actual);
918     }
919     {
920         std::vector<LineBreakExpectation> expect = {
921                 {"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
922         };
923 
924         MeasuredTextBuilder builder;
925         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
926         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
927                                           DESCENT);
928         std::unique_ptr<MeasuredText> measuredText = builder.build(
929                 textBuf, true /* compute hyphenation */, false /* compute full layout */,
930                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
931         const auto actual =
932                 doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
933         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
934                                                    << " vs " << std::endl
935                                                    << toString(textBuf, actual);
936     }
937 }
938 
TEST_F(OptimalLineBreakerTest,testEmailOrUrl)939 TEST_F(OptimalLineBreakerTest, testEmailOrUrl) {
940     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
941     constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
942     constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
943     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
944 
945     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
946     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
947     {
948         constexpr float LINE_WIDTH = 240;
949         const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
950         // clang-format off
951         std::vector<LineBreakExpectation> expect = {
952                 // TODO: Fix this. Prefer not to break inside URL.
953                 {"This is an url: http://a", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
954                 {".b",                        20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
955         };
956         // clang-format on
957         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
958         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
959 
960         // clang-format off
961         expect = {
962                 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
963                 {"http://a.b",       100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
964         };
965         // clang-format on
966         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
967         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
968     }
969     {
970         constexpr float LINE_WIDTH = 240;
971         const auto textBuf = utf8ToUtf16("This is an email: [email protected]");
972         // clang-format off
973         std::vector<LineBreakExpectation> expect = {
974                 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
975                 {"[email protected]"     , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
976         };
977         // clang-format on
978 
979         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
980         expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
981         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
982         expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
983     }
984 }
985 
TEST_F(OptimalLineBreakerTest,testLocaleSwitch_InEmailOrUrl)986 TEST_F(OptimalLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
987     constexpr float CHAR_WIDTH = 10.0;
988     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
989     constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
990     constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
991     constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
992 
993     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
994     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
995 
996     constexpr float LINE_WIDTH = 240;
997     {
998         const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
999         MeasuredTextBuilder builder;
1000         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1001         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1002                                           DESCENT);
1003         std::unique_ptr<MeasuredText> measured = builder.build(
1004                 textBuf, true /* compute hyphenation */, false /* compute full layout */,
1005                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1006 
1007         // clang-format off
1008         std::vector<LineBreakExpectation> expect = {
1009                 // TODO: Fix this. Prefer not to break inside URL.
1010                 {"This is an url: http://a", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1011                 {".b",                        20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1012         };
1013         // clang-format on
1014 
1015         auto actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
1016         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1017                                                    << " vs " << std::endl
1018                                                    << toString(textBuf, actual);
1019         actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1020         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1021                                                    << " vs " << std::endl
1022                                                    << toString(textBuf, actual);
1023 
1024         // clang-format off
1025         expect = {
1026                 {"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1027                 {"http://a.b",       100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1028         };
1029         // clang-format on
1030 
1031         actual = doLineBreak(textBuf, *measured, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1032         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1033                                                    << " vs " << std::endl
1034                                                    << toString(textBuf, actual);
1035         actual = doLineBreak(textBuf, *measured, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1036         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1037                                                    << " vs " << std::endl
1038                                                    << toString(textBuf, actual);
1039     }
1040     {
1041         const auto textBuf = utf8ToUtf16("This is an email: [email protected]");
1042         MeasuredTextBuilder builder;
1043         builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1044         builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
1045                                           DESCENT);
1046         std::unique_ptr<MeasuredText> measured = builder.build(
1047                 textBuf, true /* compute hyphenation */, false /* compute full layout */,
1048                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1049 
1050         // clang-format off
1051         std::vector<LineBreakExpectation> expect = {
1052                 {"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1053                 {"[email protected]",      130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1054         };
1055         // clang-format on
1056 
1057         auto actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, 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, *measured, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
1062         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1063                                                    << " vs " << std::endl
1064                                                    << toString(textBuf, actual);
1065         actual = doLineBreak(textBuf, *measured, BALANCED, NO_HYPHENATION, LINE_WIDTH);
1066         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1067                                                    << " vs " << std::endl
1068                                                    << toString(textBuf, actual);
1069         actual = doLineBreak(textBuf, *measured, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
1070         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1071                                                    << " vs " << std::endl
1072                                                    << toString(textBuf, actual);
1073     }
1074 }
1075 
TEST_F(OptimalLineBreakerTest,ExtentTest)1076 TEST_F(OptimalLineBreakerTest, ExtentTest) {
1077     constexpr HyphenationFrequency NO_HYPHEN = HyphenationFrequency::None;
1078     const std::vector<uint16_t> textBuf = utf8ToUtf16("The \u3042\u3044\u3046 is Japanese.");
1079 
1080     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1081     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1082     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1083     {
1084         constexpr float LINE_WIDTH = 1000;
1085         std::vector<LineBreakExpectation> expect = {
1086                 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
1087                  CUSTOM_ASCENT, CUSTOM_DESCENT},
1088         };
1089 
1090         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
1091     }
1092     {
1093         constexpr float LINE_WIDTH = 200;
1094         std::vector<LineBreakExpectation> expect = {
1095                 {"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
1096                  CUSTOM_ASCENT, CUSTOM_DESCENT},
1097         };
1098 
1099         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
1100     }
1101     {
1102         constexpr float LINE_WIDTH = 190;
1103         std::vector<LineBreakExpectation> expect = {
1104                 {"The \u3042\u3044\u3046 is ", 100, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1105                  CUSTOM_DESCENT},
1106                 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1107         };
1108         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
1109     }
1110     {
1111         constexpr float LINE_WIDTH = 90;
1112         std::vector<LineBreakExpectation> expect = {
1113                 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1114                 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1115                  CUSTOM_DESCENT},
1116                 {"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1117         };
1118         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
1119     }
1120     {
1121         constexpr float LINE_WIDTH = 50;
1122         std::vector<LineBreakExpectation> expect = {
1123                 {"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1124                 {"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1125                  CUSTOM_DESCENT},
1126                 {"Japan", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1127                 {"ese.", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1128         };
1129         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
1130     }
1131     {
1132         constexpr float LINE_WIDTH = 40;
1133         std::vector<LineBreakExpectation> expect = {
1134                 {"The ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1135                 {"\u3042\u3044", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1136                 {"\u3046 is ", 40, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1137                 {"Japa", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1138                 {"nese", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1139                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1140         };
1141         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
1142     }
1143     {
1144         constexpr float LINE_WIDTH = 20;
1145         std::vector<LineBreakExpectation> expect = {
1146                 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1147                 {"he ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1148                 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1149                 {"\u3044\u3046 ", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
1150                  CUSTOM_DESCENT},
1151                 {"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1152                 {"Ja", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1153                 {"pa", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1154                 {"ne", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1155                 {"se", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1156                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1157         };
1158         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
1159     }
1160     {
1161         constexpr float LINE_WIDTH = 10;
1162         std::vector<LineBreakExpectation> expect = {
1163                 {"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1164                 {"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1165                 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1166                 {"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1167                 {"\u3044", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1168                 {"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
1169                 {"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1170                 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1171                 {"J", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1172                 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1173                 {"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1174                 {"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1175                 {"n", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1176                 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1177                 {"s", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1178                 {"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1179                 {".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1180         };
1181         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
1182     }
1183 }
1184 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_SingleChar)1185 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
1186     constexpr float CHAR_WIDTH = 10.0;
1187 
1188     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1189     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1190 
1191     const auto textBuf = utf8ToUtf16("This is an example \u2639 text.");
1192 
1193     // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1194     auto doLineBreak = [=](float width) {
1195         MeasuredTextBuilder builder;
1196         builder.addCustomRun<ConstantRun>(Range(0, 19), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1197         builder.addReplacementRun(19, 21, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1198         builder.addCustomRun<ConstantRun>(Range(21, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1199                                           DESCENT);
1200 
1201         std::unique_ptr<MeasuredText> measuredText = builder.build(
1202                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1203                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1204         RectangleLineWidth rectangleLineWidth(width);
1205         TabStops tabStops(nullptr, 0, 0);
1206         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1207                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1208                                 false /* justified */, false /* useBoundsForWidth */);
1209     };
1210 
1211     {
1212         constexpr float LINE_WIDTH = 100;
1213         // "is an" is a single replacement span. Do not break.
1214         // clang-format off
1215         std::vector<LineBreakExpectation> expect = {
1216                 {"This is an ",   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1217                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1218                 {"\u2639 text.",  100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1219         };
1220         // clang-format on
1221         const auto actual = doLineBreak(LINE_WIDTH);
1222         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1223                                                    << " vs " << std::endl
1224                                                    << toString(textBuf, actual);
1225     }
1226     {
1227         constexpr float LINE_WIDTH = 90;
1228         // "is an" is a single replacement span. Do not break.
1229         // clang-format off
1230         std::vector<LineBreakExpectation> expect = {
1231                 {"This ",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1232                 {"is an ",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1233                 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1234                 {"\u2639 ", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1235                 {"text.",   50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1236         };
1237         // clang-format on
1238         const auto actual = doLineBreak(LINE_WIDTH);
1239         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1240                                                    << " vs " << std::endl
1241                                                    << toString(textBuf, actual);
1242     }
1243     {
1244         constexpr float LINE_WIDTH = 10;
1245         // "is an" is a single replacement span. Do not break.
1246         // clang-format off
1247         std::vector<LineBreakExpectation> expect = {
1248                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1249                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1250                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1251                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1252                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1253                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1254                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1255                 {"n ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1256                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1257                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1258                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1259                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1260                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1261                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1262                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1263                 {"\u2639 ",50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1264                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1265                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1266                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1267                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1268                 {".",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1269         };
1270         // clang-format on
1271         const auto actual = doLineBreak(LINE_WIDTH);
1272         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1273                                                    << " vs " << std::endl
1274                                                    << toString(textBuf, actual);
1275     }
1276 }
1277 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_MultipleChars)1278 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
1279     constexpr float CHAR_WIDTH = 10.0;
1280 
1281     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1282     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1283 
1284     const auto textBuf = utf8ToUtf16("This is an example text.");
1285 
1286     // In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
1287     auto doLineBreak = [=](float width) {
1288         MeasuredTextBuilder builder;
1289         builder.addCustomRun<ConstantRun>(Range(0, 5), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1290         builder.addReplacementRun(5, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1291         builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1292                                           DESCENT);
1293 
1294         std::unique_ptr<MeasuredText> measuredText = builder.build(
1295                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1296                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1297         RectangleLineWidth rectangleLineWidth(width);
1298         TabStops tabStops(nullptr, 0, 0);
1299         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1300                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1301                                 false /* justified */, false /* useBoundsForWidth */);
1302     };
1303 
1304     {
1305         constexpr float LINE_WIDTH = 100;
1306         // "is an" is a single replacement span. Do not break.
1307         // clang-format off
1308         std::vector<LineBreakExpectation> expect = {
1309                 {"This is an ",   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1310                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1311                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1312         };
1313         // clang-format on
1314         const auto actual = doLineBreak(LINE_WIDTH);
1315         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1316                                                    << " vs " << std::endl
1317                                                    << toString(textBuf, actual);
1318     }
1319     {
1320         constexpr float LINE_WIDTH = 90;
1321         // "is an" is a single replacement span. Do not break.
1322         // clang-format off
1323         std::vector<LineBreakExpectation> expect = {
1324                 {"This ",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1325                 {"is an ",  50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1326                 {"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1327                 {"text.",   50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1328         };
1329         // clang-format on
1330         const auto actual = doLineBreak(LINE_WIDTH);
1331         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1332                                                    << " vs " << std::endl
1333                                                    << toString(textBuf, actual);
1334     }
1335     {
1336         constexpr float LINE_WIDTH = 10;
1337         // "is an" is a single replacement span. Do not break.
1338         // clang-format off
1339         std::vector<LineBreakExpectation> expect = {
1340                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1341                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1342                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1343                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1344                 {"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1345                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1346                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1347                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1348                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1349                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1350                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1351                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1352                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1353                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1354                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1355                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1356                 {".",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1357         };
1358         // clang-format on
1359         const auto actual = doLineBreak(LINE_WIDTH);
1360         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1361                                                    << " vs " << std::endl
1362                                                    << toString(textBuf, actual);
1363     }
1364 }
1365 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_continuedReplacementSpan)1366 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_continuedReplacementSpan) {
1367     constexpr float CHAR_WIDTH = 10.0;
1368 
1369     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1370     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1371 
1372     const auto textBuf = utf8ToUtf16("This is an example text.");
1373 
1374     // In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
1375     auto doLineBreak = [=](float width) {
1376         MeasuredTextBuilder builder;
1377         builder.addReplacementRun(0, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1378         builder.addReplacementRun(5, 8, 3 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1379         builder.addReplacementRun(8, 11, 3 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1380         builder.addReplacementRun(11, 19, 8 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1381         builder.addReplacementRun(19, 24, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1382         std::unique_ptr<MeasuredText> measuredText = builder.build(
1383                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1384                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1385         RectangleLineWidth rectangleLineWidth(width);
1386         TabStops tabStops(nullptr, 0, 0);
1387         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1388                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1389                                 false /* justified */, false /* useBoundsForWidth */);
1390     };
1391 
1392     {
1393         constexpr float LINE_WIDTH = 100;
1394         // clang-format off
1395         std::vector<LineBreakExpectation> expect = {
1396                 {"This ",    50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1397                 {"is an ",   60, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1398                 {"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1399                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1400         };
1401         // clang-format on
1402         const auto actual = doLineBreak(LINE_WIDTH);
1403         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1404                                                    << " vs " << std::endl
1405                                                    << toString(textBuf, actual);
1406     }
1407     {
1408         constexpr float LINE_WIDTH = 40;
1409         // clang-format off
1410         std::vector<LineBreakExpectation> expect = {
1411                 {"This ",    50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1412                 {"is ",      30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1413                 {"an ",      30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1414                 {"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1415                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1416         };
1417         // clang-format on
1418         const auto actual = doLineBreak(LINE_WIDTH);
1419         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1420                                                    << " vs " << std::endl
1421                                                    << toString(textBuf, actual);
1422     }
1423     {
1424         constexpr float LINE_WIDTH = 10;
1425         // clang-format off
1426         std::vector<LineBreakExpectation> expect = {
1427                 {"This ",    50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1428                 {"is ",      30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1429                 {"an ",      30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1430                 {"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1431                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
1432         };
1433         // clang-format on
1434         const auto actual = doLineBreak(LINE_WIDTH);
1435         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1436                                                    << " vs " << std::endl
1437                                                    << toString(textBuf, actual);
1438     }
1439 }
1440 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_CJK)1441 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
1442     constexpr float CHAR_WIDTH = 10.0;
1443 
1444     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1445     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1446 
1447     // Example string: "Today is a sunny day." in Japanese.
1448     const auto textBuf = utf8ToUtf16("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A");
1449 
1450     // In this test case, assign a replacement run for "\u6674\u5929" with 5 times of CHAR_WIDTH.
1451     auto doLineBreak = [=](float width) {
1452         MeasuredTextBuilder builder;
1453         builder.addCustomRun<ConstantRun>(Range(0, 3), "ja-JP", CHAR_WIDTH, ASCENT, DESCENT);
1454         builder.addReplacementRun(3, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("ja-JP"));
1455         builder.addCustomRun<ConstantRun>(Range(5, textBuf.size()), "ja-JP", CHAR_WIDTH, ASCENT,
1456                                           DESCENT);
1457 
1458         std::unique_ptr<MeasuredText> measuredText = builder.build(
1459                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1460                 false /* computeBounds */, false /* ignore krening */, nullptr /* no hint */);
1461         RectangleLineWidth rectangleLineWidth(width);
1462         TabStops tabStops(nullptr, 0, 0);
1463         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1464                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1465                                 false /* justified */, false /* useBoundsForWidth */);
1466     };
1467 
1468     {
1469         constexpr float LINE_WIDTH = 100;
1470         // "\u6674\u5929" is a single replacement span. Do not break.
1471         // clang-format off
1472         std::vector<LineBreakExpectation> expect = {
1473                 {"\u672C\u65E5\u306F\u6674\u5929\u306A\u308A",
1474                   100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1475         };
1476         // clang-format on
1477         const auto actual = doLineBreak(LINE_WIDTH);
1478         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1479                                                    << " vs " << std::endl
1480                                                    << toString(textBuf, actual);
1481     }
1482     {
1483         constexpr float LINE_WIDTH = 90;
1484         // "\u6674\u5929" is a single replacement span. Do not break.
1485         // clang-format off
1486         std::vector<LineBreakExpectation> expect = {
1487                 {"\u672C\u65E5\u306F\u6674\u5929\u306A",
1488                   90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1489                 {"\u308A",
1490                   10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1491         };
1492         // clang-format on
1493         const auto actual = doLineBreak(LINE_WIDTH);
1494         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1495                                                    << " vs " << std::endl
1496                                                    << toString(textBuf, actual);
1497     }
1498     {
1499         constexpr float LINE_WIDTH = 80;
1500         // "\u6674\u5929" is a single replacement span. Do not break.
1501         // clang-format off
1502         std::vector<LineBreakExpectation> expect = {
1503                 {"\u672C\u65E5\u306F\u6674\u5929",
1504                   80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1505                 {"\u306A\u308A",
1506                   20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1507         };
1508         // clang-format on
1509         const auto actual = doLineBreak(LINE_WIDTH);
1510         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1511                                                    << " vs " << std::endl
1512                                                    << toString(textBuf, actual);
1513     }
1514     {
1515         constexpr float LINE_WIDTH = 70;
1516         // "\u6674\u5929" is a single replacement span. Do not break.
1517         // clang-format off
1518         std::vector<LineBreakExpectation> expect = {
1519                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1520                 {"\u6674\u5929\u306A\u308A", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1521         };
1522         // clang-format on
1523         const auto actual = doLineBreak(LINE_WIDTH);
1524         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1525                                                    << " vs " << std::endl
1526                                                    << toString(textBuf, actual);
1527     }
1528     {
1529         constexpr float LINE_WIDTH = 60;
1530         // "\u6674\u5929" is a single replacement span. Do not break.
1531         // clang-format off
1532         std::vector<LineBreakExpectation> expect = {
1533                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1534                 {"\u6674\u5929\u306A", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1535                 {"\u308A",             10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1536         };
1537         // clang-format on
1538         const auto actual = doLineBreak(LINE_WIDTH);
1539         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1540                                                    << " vs " << std::endl
1541                                                    << toString(textBuf, actual);
1542     }
1543     {
1544         constexpr float LINE_WIDTH = 50;
1545         // "\u6674\u5929" is a single replacement span. Do not break.
1546         // clang-format off
1547         std::vector<LineBreakExpectation> expect = {
1548                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1549                 {"\u6674\u5929",       50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1550                 {"\u306A\u308A",       20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1551         };
1552         // clang-format on
1553         const auto actual = doLineBreak(LINE_WIDTH);
1554         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1555                                                    << " vs " << std::endl
1556                                                    << toString(textBuf, actual);
1557     }
1558     {
1559         constexpr float LINE_WIDTH = 40;
1560         // "\u6674\u5929" is a single replacement span. Do not break.
1561         // clang-format off
1562         std::vector<LineBreakExpectation> expect = {
1563                 {"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1564                 {"\u6674\u5929",       50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1565                 {"\u306A\u308A",       20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1566         };
1567         // clang-format on
1568         const auto actual = doLineBreak(LINE_WIDTH);
1569         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1570                                                    << " vs " << std::endl
1571                                                    << toString(textBuf, actual);
1572     }
1573     {
1574         constexpr float LINE_WIDTH = 10;
1575         // "\u6674\u5929" is a single replacement span. Do not break.
1576         // clang-format off
1577         std::vector<LineBreakExpectation> expect = {
1578                 {"\u672C",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1579                 {"\u65E5",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1580                 {"\u306F",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1581                 {"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1582                 {"\u306A",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1583                 {"\u308A",       10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1584         };
1585         // clang-format on
1586         const auto actual = doLineBreak(LINE_WIDTH);
1587         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1588                                                    << " vs " << std::endl
1589                                                    << toString(textBuf, actual);
1590     }
1591 }
1592 
1593 // http://b/119657685
1594 // Following test case is for verifying that the ReplacementSpan should not be broken into multiple
1595 // pieces. The actual break point is not a part of expectation. For example, it would be good to
1596 // break the starting offset of the ReplacementSpan for some case.
TEST_F(OptimalLineBreakerTest,testReplacementSpan_GraphemeLineBreakWithMultipleRepalcementSpans)1597 TEST_F(OptimalLineBreakerTest, testReplacementSpan_GraphemeLineBreakWithMultipleRepalcementSpans) {
1598     constexpr float CHAR_WIDTH = 10.0;
1599 
1600     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1601     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1602 
1603     const auto textBuf = utf8ToUtf16("ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0pq st");
1604 
1605     auto doLineBreak = [=](float width) {
1606         MeasuredTextBuilder builder;
1607         builder.addReplacementRun(0, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1608         builder.addCustomRun<ConstantRun>(Range(5, 7), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1609         builder.addReplacementRun(7, 12, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1610         builder.addCustomRun<ConstantRun>(Range(12, 14), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1611         builder.addReplacementRun(14, 19, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1612         builder.addCustomRun<ConstantRun>(Range(19, 21), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1613         builder.addReplacementRun(21, 26, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1614 
1615         std::unique_ptr<MeasuredText> measuredText = builder.build(
1616                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1617                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1618         RectangleLineWidth rectangleLineWidth(width);
1619         TabStops tabStops(nullptr, 0, 0);
1620         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1621                                 BreakStrategy::HighQuality, HyphenationFrequency::None,
1622                                 false /* justified */, false /* useBoundsForWidth */);
1623     };
1624 
1625     {
1626         constexpr float LINE_WIDTH = 1000;
1627         // clang-format off
1628         std::vector<LineBreakExpectation> expect = {
1629                 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0pq st",
1630                   260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1631         };
1632         // clang-format on
1633         const auto actual = doLineBreak(LINE_WIDTH);
1634         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1635                                                    << " vs " << std::endl
1636                                                    << toString(textBuf, actual);
1637     }
1638     {
1639         constexpr float LINE_WIDTH = 250;
1640         // clang-format off
1641         std::vector<LineBreakExpectation> expect = {
1642                 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0",
1643                   210, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1644                 {"pq st",
1645                    50, NO_START_HYPHEN, NO_END_HYPHEN,       0,      0},
1646         };
1647         // clang-format on
1648         const auto actual = doLineBreak(LINE_WIDTH);
1649         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1650                                                    << " vs " << std::endl
1651                                                    << toString(textBuf, actual);
1652     }
1653     {
1654         constexpr float LINE_WIDTH = 180;
1655         // clang-format off
1656         std::vector<LineBreakExpectation> expect = {
1657                 {"ab de\u00A0\u00A0fg ij\u00A0\u00A0",
1658                   140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1659                 {"kl no\u00A0\u00A0pq st",
1660                   120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1661         };
1662         // clang-format on
1663         const auto actual = doLineBreak(LINE_WIDTH);
1664         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1665                                                    << " vs " << std::endl
1666                                                    << toString(textBuf, actual);
1667     }
1668     {
1669         constexpr float LINE_WIDTH = 130;
1670         // clang-format off
1671         std::vector<LineBreakExpectation> expect = {
1672                 {"ab de\u00A0\u00A0fg ij\u00A0",
1673                   130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1674                 {"\u00A0kl no\u00A0\u00A0pq st",
1675                   130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1676         };
1677         // clang-format on
1678         const auto actual = doLineBreak(LINE_WIDTH);
1679         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1680                                                    << " vs " << std::endl
1681                                                    << toString(textBuf, actual);
1682     }
1683     {
1684         constexpr float LINE_WIDTH = 110;
1685         // clang-format off
1686         std::vector<LineBreakExpectation> expect = {
1687                 {"ab de\u00A0",             60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1688                 {"\u00A0fg ij\u00A0\u00A0", 80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1689                 {"kl no\u00A0\u00A0",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1690                 {"pq st",                   50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1691         };
1692         // clang-format on
1693         const auto actual = doLineBreak(LINE_WIDTH);
1694         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1695                                                    << " vs " << std::endl
1696                                                    << toString(textBuf, actual);
1697     }
1698     {
1699         constexpr float LINE_WIDTH = 60;
1700         // clang-format off
1701         std::vector<LineBreakExpectation> expect = {
1702                 {"ab de\u00A0",  60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1703                 {"\u00A0fg ij",  60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1704                 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1705                 {"kl no\u00A0",  60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1706                 {"\u00A0pq st",  60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1707         };
1708         // clang-format on
1709         const auto actual = doLineBreak(LINE_WIDTH);
1710         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1711                                                    << " vs " << std::endl
1712                                                    << toString(textBuf, actual);
1713     }
1714     {
1715         constexpr float LINE_WIDTH = 50;
1716         // clang-format off
1717         std::vector<LineBreakExpectation> expect = {
1718                 {"ab de",        50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1719                 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1720                 {"fg ij",        50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1721                 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1722                 {"kl no",        50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1723                 {"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1724                 {"pq st",        50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1725         };
1726         // clang-format on
1727         const auto actual = doLineBreak(LINE_WIDTH);
1728         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1729                                                    << " vs " << std::endl
1730                                                    << toString(textBuf, actual);
1731     }
1732 }
1733 
TEST_F(OptimalLineBreakerTest,testReplacementSpanNotBreakTest_with_punctuation)1734 TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation) {
1735     constexpr float CHAR_WIDTH = 10.0;
1736 
1737     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1738     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1739 
1740     const auto textBuf = utf8ToUtf16("This (is an) example text.");
1741 
1742     // In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
1743     auto doLineBreak = [=](float width) {
1744         MeasuredTextBuilder builder;
1745         builder.addCustomRun<ConstantRun>(Range(0, 6), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
1746         builder.addReplacementRun(6, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
1747         builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
1748                                           DESCENT);
1749 
1750         std::unique_ptr<MeasuredText> measuredText = builder.build(
1751                 textBuf, false /* compute hyphenation */, false /* compute full layout */,
1752                 false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1753         RectangleLineWidth rectangleLineWidth(width);
1754         TabStops tabStops(nullptr, 0, 0);
1755         return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
1756                                 BreakStrategy::HighQuality, HyphenationFrequency::Normal,
1757                                 false /* justified */, false /* useBoundsForWidth */);
1758     };
1759 
1760     {
1761         constexpr float LINE_WIDTH = 1000;
1762         // "is an" is a single replacement span. Do not break.
1763         // clang-format off
1764         std::vector<LineBreakExpectation> expect = {
1765                 {"This (is an) example text.",
1766                   260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1767         };
1768         // clang-format on
1769         const auto actual = doLineBreak(LINE_WIDTH);
1770         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1771                                                    << " vs " << std::endl
1772                                                    << toString(textBuf, actual);
1773     }
1774     {
1775         constexpr float LINE_WIDTH = 250;
1776         // "is an" is a single replacement span. Do not break.
1777         // clang-format off
1778         std::vector<LineBreakExpectation> expect = {
1779                 {"This (is an) example ", 200, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1780                 {"text.",                  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1781         };
1782         // clang-format on
1783         const auto actual = doLineBreak(LINE_WIDTH);
1784         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1785                                                    << " vs " << std::endl
1786                                                    << toString(textBuf, actual);
1787     }
1788     {
1789         constexpr float LINE_WIDTH = 190;
1790         // "is an" is a single replacement span. Do not break.
1791         // clang-format off
1792         std::vector<LineBreakExpectation> expect = {
1793                 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1794                 {"example text.", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1795         };
1796         // clang-format on
1797         const auto actual = doLineBreak(LINE_WIDTH);
1798         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1799                                                    << " vs " << std::endl
1800                                                    << toString(textBuf, actual);
1801     }
1802     {
1803         constexpr float LINE_WIDTH = 120;
1804         // "is an" is a single replacement span. Do not break.
1805         // clang-format off
1806         std::vector<LineBreakExpectation> expect = {
1807                 {"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1808                 {"example ",       70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1809                 {"text.",          50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1810         };
1811         // clang-format on
1812         const auto actual = doLineBreak(LINE_WIDTH);
1813         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1814                                                    << " vs " << std::endl
1815                                                    << toString(textBuf, actual);
1816     }
1817     {
1818         constexpr float LINE_WIDTH = 110;
1819         // "is an" is a single replacement span. Do not break.
1820         // clang-format off
1821         std::vector<LineBreakExpectation> expect = {
1822                 {"This ",    40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1823                 {"(is an) ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1824                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1825                 {"text.",    50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1826         };
1827         // clang-format on
1828         const auto actual = doLineBreak(LINE_WIDTH);
1829         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1830                                                    << " vs " << std::endl
1831                                                    << toString(textBuf, actual);
1832     }
1833     {
1834         constexpr float LINE_WIDTH = 60;
1835         // "is an" is a single replacement span. Do not break.
1836         // clang-format off
1837         std::vector<LineBreakExpectation> expect = {
1838                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1839                 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1840                 {") ex",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1841                 {"ample ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1842                 {"text.",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1843         };
1844         // clang-format on
1845         const auto actual = doLineBreak(LINE_WIDTH);
1846         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1847                                                    << " vs " << std::endl
1848                                                    << toString(textBuf, actual);
1849     }
1850     {
1851         constexpr float LINE_WIDTH = 50;
1852         // "is an" is a single replacement span. Do not break.
1853         // clang-format off
1854         std::vector<LineBreakExpectation> expect = {
1855                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1856                 {"(",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1857                 {"is an",  50, NO_START_HYPHEN, NO_END_HYPHEN,      0,       0},
1858                 {") ex",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1859                 {"ample ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1860                 {"text.",  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1861         };
1862         // clang-format on
1863         const auto actual = doLineBreak(LINE_WIDTH);
1864         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1865                                                    << " vs " << std::endl
1866                                                    << toString(textBuf, actual);
1867     }
1868     {
1869         constexpr float LINE_WIDTH = 40;
1870         // "is an" is a single replacement span. Do not break.
1871         // clang-format off
1872         std::vector<LineBreakExpectation> expect = {
1873                 {"This ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1874                 // TODO(nona): This might be wrongly broken. "(is an" should be broken into "(" and
1875                 // "is an" as the desperate break.
1876                 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1877                 {") ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1878                 {"exa",    30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1879                 {"mple ",  40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1880                 {"text",   40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1881                 {".",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1882         };
1883         // clang-format on
1884         const auto actual = doLineBreak(LINE_WIDTH);
1885         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1886                                                    << " vs " << std::endl
1887                                                    << toString(textBuf, actual);
1888     }
1889     {
1890         constexpr float LINE_WIDTH = 10;
1891         // "is an" is a single replacement span. Do not break.
1892         // clang-format off
1893         std::vector<LineBreakExpectation> expect = {
1894                 {"T",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1895                 {"h",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1896                 {"i",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1897                 {"s ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1898                 // TODO(nona): This might be wrongly broken. "(is an" should be broken into "(" and
1899                 // "is an" as the desperate break.
1900                 {"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1901                 {") ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1902                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1903                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1904                 {"a",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1905                 {"m",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1906                 {"p",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1907                 {"l",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1908                 {"e ",     10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1909                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1910                 {"e",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1911                 {"x",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1912                 {"t",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1913                 {".",      10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1914         };
1915         // clang-format on
1916         const auto actual = doLineBreak(LINE_WIDTH);
1917         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1918                                                    << " vs " << std::endl
1919                                                    << toString(textBuf, actual);
1920     }
1921 }
1922 
TEST_F(OptimalLineBreakerTest,testControllCharAfterSpace)1923 TEST_F(OptimalLineBreakerTest, testControllCharAfterSpace) {
1924     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
1925     constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
1926     constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
1927     const std::vector<uint16_t> textBuf = utf8ToUtf16("example \u2066example");
1928 
1929     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1930     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1931     {
1932         constexpr float LINE_WIDTH = 90;
1933         // Note that HarfBuzz assigns 0px for control characters regardless of glyph existence in
1934         // the font.
1935         std::vector<LineBreakExpectation> expect = {
1936                 {"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1937                 {"\u2066example", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1938         };
1939 
1940         expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
1941         expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
1942     }
1943 }
1944 
TEST_F(OptimalLineBreakerTest,roundingError)1945 TEST_F(OptimalLineBreakerTest, roundingError) {
1946     MeasuredTextBuilder builder;
1947     auto family1 = buildFontFamily("Ascii.ttf");
1948     std::vector<std::shared_ptr<FontFamily>> families = {family1};
1949     auto fc = FontCollection::create(families);
1950     MinikinPaint paint(fc);
1951     paint.size = 56.0f;  // Make 1em=56px
1952     paint.scaleX = 1;
1953     paint.letterSpacing = -0.093f;
1954     paint.localeListId = LocaleListCache::getId("en-US");
1955     const std::vector<uint16_t> textBuffer = utf8ToUtf16("8888888888888888888");
1956 
1957     float measured =
1958             Layout::measureText(textBuffer, Range(0, textBuffer.size()), Bidi::LTR, paint,
1959                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, nullptr,
1960                                 nullptr /* bounds */, nullptr /* cluster count */, RunFlag::NONE);
1961 
1962     builder.addStyleRun(0, textBuffer.size(), std::move(paint), 0, 0, true, false);
1963     std::unique_ptr<MeasuredText> measuredText = builder.build(
1964             textBuffer, false /* compute hyphenation */, false /* compute full layout */,
1965             false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
1966     RectangleLineWidth rectangleLineWidth(measured);
1967     TabStops tabStops(nullptr, 0, 10);
1968     LineBreakResult r = doLineBreak(textBuffer, *measuredText, BreakStrategy::Balanced,
1969                                     HyphenationFrequency::None, measured);
1970 
1971     EXPECT_EQ(1u, r.breakPoints.size());
1972 }
1973 
TEST_F(OptimalLineBreakerTest,testBreakWithoutBounds_trailing)1974 TEST_F(OptimalLineBreakerTest, testBreakWithoutBounds_trailing) {
1975     // The OvershootTest.ttf has following coverage, extent, width and bbox.
1976     // U+0061(a): 1em, (   0, 0) - (1,   1)
1977     // U+0062(b): 1em, (   0, 0) - (1.5, 1)
1978     // U+0063(c): 1em, (   0, 0) - (2,   1)
1979     // U+0064(d): 1em, (   0, 0) - (2.5, 1)
1980     // U+0065(e): 1em, (-0.5, 0) - (1,   1)
1981     // U+0066(f): 1em, (-1.0, 0) - (1,   1)
1982     // U+0067(g): 1em, (-1.5, 0) - (1,   1)
1983     const std::vector<uint16_t> textBuf = utf8ToUtf16("dddd dddd dddd dddd");
1984     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
1985     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
1986     // Note that disable clang-format everywhere since aligned expectation is more readable.
1987     {
1988         constexpr float LINE_WIDTH = 1000;
1989         // clang-format off
1990         std::vector<LineBreakExpectation> expect = {
1991                 {"dddd dddd dddd dddd", 190, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
1992         };
1993         // clang-format on
1994 
1995         const auto actual = doLineBreakForBounds(textBuf, BreakStrategy::HighQuality,
1996                                                  HyphenationFrequency::None, LINE_WIDTH);
1997         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
1998                                                    << " vs " << std::endl
1999                                                    << toString(textBuf, actual);
2000         EXPECT_EQ(MinikinRect(0, -10, 205, 0), actual.bounds[0]);
2001     }
2002     {
2003         constexpr float LINE_WIDTH = 110;
2004         // clang-format off
2005         std::vector<LineBreakExpectation> expect = {
2006                 {"dddd dddd ", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2007                 {"dddd dddd", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2008         };
2009         // clang-format on
2010 
2011         const auto actual = doLineBreakForBounds(textBuf, BreakStrategy::HighQuality,
2012                                                  HyphenationFrequency::None, LINE_WIDTH);
2013         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2014                                                    << " vs " << std::endl
2015                                                    << toString(textBuf, actual);
2016         EXPECT_EQ(MinikinRect(0, -10, 105, 0), actual.bounds[0]);
2017         EXPECT_EQ(MinikinRect(0, -10, 105, 0), actual.bounds[1]);
2018     }
2019     {
2020         constexpr float LINE_WIDTH = 100;
2021         // Even if the total advance of "dddd dddd" is 90, the width of bounding box of "dddd dddd"
2022         // is
2023         // Rect(0em, 1em, 10.5em, 0em). So "dddd dddd" is broken into two lines.
2024         // clang-format off
2025         std::vector<LineBreakExpectation> expect = {
2026                 {"dddd ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2027                 {"dddd ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2028                 {"dddd ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2029                 {"dddd", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2030         };
2031         // clang-format on
2032 
2033         const auto actual = doLineBreakForBounds(textBuf, BreakStrategy::HighQuality,
2034                                                  HyphenationFrequency::None, LINE_WIDTH);
2035         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2036                                                    << " vs " << std::endl
2037                                                    << toString(textBuf, actual);
2038         EXPECT_EQ(MinikinRect(0, -10, 55, 0), actual.bounds[0]);
2039         EXPECT_EQ(MinikinRect(0, -10, 55, 0), actual.bounds[1]);
2040         EXPECT_EQ(MinikinRect(0, -10, 55, 0), actual.bounds[2]);
2041         EXPECT_EQ(MinikinRect(0, -10, 55, 0), actual.bounds[3]);
2042     }
2043 }
2044 
TEST_F(OptimalLineBreakerTest,testBreakWithoutBounds_preceding)2045 TEST_F(OptimalLineBreakerTest, testBreakWithoutBounds_preceding) {
2046     // The OvershootTest.ttf has following coverage, extent, width and bbox.
2047     // U+0061(a): 1em, (   0, 0) - (1,   1)
2048     // U+0062(b): 1em, (   0, 0) - (1.5, 1)
2049     // U+0063(c): 1em, (   0, 0) - (2,   1)
2050     // U+0064(d): 1em, (   0, 0) - (2.5, 1)
2051     // U+0065(e): 1em, (-0.5, 0) - (1,   1)
2052     // U+0066(f): 1em, (-1.0, 0) - (1,   1)
2053     // U+0067(g): 1em, (-1.5, 0) - (1,   1)
2054     const std::vector<uint16_t> textBuf = utf8ToUtf16("gggg gggg gggg gggg");
2055     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
2056     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
2057     // Note that disable clang-format everywhere since aligned expectation is more readable.
2058     {
2059         constexpr float LINE_WIDTH = 1000;
2060         // clang-format off
2061         std::vector<LineBreakExpectation> expect = {
2062                 {"gggg gggg gggg gggg", 190, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2063         };
2064         // clang-format on
2065 
2066         const auto actual = doLineBreakForBounds(textBuf, BreakStrategy::HighQuality,
2067                                                  HyphenationFrequency::None, LINE_WIDTH);
2068         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2069                                                    << " vs " << std::endl
2070                                                    << toString(textBuf, actual);
2071         EXPECT_EQ(MinikinRect(-15, -10, 190, 0), actual.bounds[0]);
2072     }
2073     {
2074         constexpr float LINE_WIDTH = 110;
2075         // clang-format off
2076         std::vector<LineBreakExpectation> expect = {
2077                 {"gggg gggg ", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2078                 {"gggg gggg", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2079         };
2080         // clang-format on
2081 
2082         const auto actual = doLineBreakForBounds(textBuf, BreakStrategy::HighQuality,
2083                                                  HyphenationFrequency::None, LINE_WIDTH);
2084         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2085                                                    << " vs " << std::endl
2086                                                    << toString(textBuf, actual);
2087         EXPECT_EQ(MinikinRect(-15, -10, 90, 0), actual.bounds[0]);
2088         EXPECT_EQ(MinikinRect(-15, -10, 90, 0), actual.bounds[1]);
2089     }
2090     {
2091         constexpr float LINE_WIDTH = 100;
2092         // Even if the total advance of "gggg gggg" is 90, the width of bounding box of "gggg gggg"
2093         // is
2094         // Rect(0em, 1em, 10.5em, 0em). So "gggg gggg" is broken into two lines.
2095         // clang-format off
2096         std::vector<LineBreakExpectation> expect = {
2097                 {"gggg ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2098                 {"gggg ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2099                 {"gggg ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2100                 {"gggg", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2101         };
2102         // clang-format on
2103 
2104         const auto actual = doLineBreakForBounds(textBuf, BreakStrategy::HighQuality,
2105                                                  HyphenationFrequency::None, LINE_WIDTH);
2106         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2107                                                    << " vs " << std::endl
2108                                                    << toString(textBuf, actual);
2109         EXPECT_EQ(MinikinRect(-15, -10, 40, 0), actual.bounds[0]);
2110         EXPECT_EQ(MinikinRect(-15, -10, 40, 0), actual.bounds[1]);
2111         EXPECT_EQ(MinikinRect(-15, -10, 40, 0), actual.bounds[2]);
2112         EXPECT_EQ(MinikinRect(-15, -10, 40, 0), actual.bounds[3]);
2113     }
2114 }
2115 
TEST_F(OptimalLineBreakerTest,testBreakWithHyphenation_NoHyphenSpan)2116 TEST_F(OptimalLineBreakerTest, testBreakWithHyphenation_NoHyphenSpan) {
2117     const std::vector<uint16_t> textBuffer = utf8ToUtf16("This is Android. Here is hyphenation.");
2118     const Range noHyphenRange(25, 37);  // the range of the word "hyphenation".
2119 
2120     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
2121     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
2122     constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
2123     // Note that disable clang-format everywhere since aligned expectation is more readable.
2124     {
2125         constexpr float LINE_WIDTH = 170;
2126         // clang-format off
2127         std::vector<LineBreakExpectation> expect = {
2128                 { "This is Android. " , 160, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
2129                 { "Here is hyphena-"  , 160, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT },
2130                 { "tion."             ,  50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
2131         };
2132         // clang-format on
2133 
2134         auto actual =
2135                 doLineBreak(textBuffer, BreakStrategy::HighQuality, HyphenationFrequency::Normal,
2136                             "en-US", LINE_WIDTH, false /* ignore kerning */);
2137         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2138                                                    << " vs " << std::endl
2139                                                    << toString(textBuffer, actual);
2140     }
2141     {
2142         constexpr float LINE_WIDTH = 170;
2143         // clang-format off
2144         std::vector<LineBreakExpectation> expect = {
2145                 { "This is An-"    , 110, NO_START_HYPHEN,    END_HYPHEN, ASCENT, DESCENT },
2146                 { "droid. Here is ", 140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
2147                 { "hyphenation."   , 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
2148         };
2149         // clang-format on
2150 
2151         auto actual = doLineBreakWithNoHyphenSpan(textBuffer, noHyphenRange, LINE_WIDTH);
2152         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2153                                                    << " vs " << std::endl
2154                                                    << toString(textBuffer, actual);
2155     }
2156 }
2157 
TEST_F(OptimalLineBreakerTest,testPhraseBreakNone)2158 TEST_F(OptimalLineBreakerTest, testPhraseBreakNone) {
2159     // For short hand of writing expectation for lines.
2160     auto line = [](std::string t, float w) -> LineBreakExpectation {
2161         return {t, w, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, ASCENT, DESCENT};
2162     };
2163 
2164     // Note that disable clang-format everywhere since aligned expectation is more readable.
2165     {
2166         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 1));
2167         constexpr float LINE_WIDTH = 100;
2168         // clang-format off
2169         std::vector<LineBreakExpectation> expect = {
2170                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002" , 80),
2171         };
2172         // clang-format on
2173 
2174         const auto actual =
2175                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
2176         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2177                                                    << " vs " << std::endl
2178                                                    << toString(textBuf, actual);
2179     }
2180     {
2181         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 2));
2182         constexpr float LINE_WIDTH = 100;
2183         // clang-format off
2184         std::vector<LineBreakExpectation> expect = {
2185                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5" , 100),
2186                 line("\u306F\u6674\u5929\u306A\u308A\u3002" , 60),
2187         };
2188         // clang-format on
2189 
2190         const auto actual =
2191                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
2192         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2193                                                    << " vs " << std::endl
2194                                                    << toString(textBuf, actual);
2195     }
2196     {
2197         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 3));
2198         constexpr float LINE_WIDTH = 100;
2199         // clang-format off
2200         std::vector<LineBreakExpectation> expect = {
2201                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2202                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2203                 line("\u5929\u306A\u308A\u3002", 40),
2204         };
2205         // clang-format on
2206 
2207         const auto actual =
2208                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
2209         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2210                                                    << " vs " << std::endl
2211                                                    << toString(textBuf, actual);
2212     }
2213     {
2214         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 4));
2215         constexpr float LINE_WIDTH = 100;
2216         // clang-format off
2217         std::vector<LineBreakExpectation> expect = {
2218                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2219                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2220                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2221                 line("\u308A\u3002"  , 20),
2222         };
2223         // clang-format on
2224 
2225         const auto actual =
2226                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
2227         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2228                                                    << " vs " << std::endl
2229                                                    << toString(textBuf, actual);
2230     }
2231     {
2232         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 5));
2233         constexpr float LINE_WIDTH = 100;
2234         // clang-format off
2235         std::vector<LineBreakExpectation> expect = {
2236                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2237                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2238                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2239                 line("\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 100),
2240         };
2241         // clang-format on
2242 
2243         const auto actual =
2244                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
2245         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2246                                                    << " vs " << std::endl
2247                                                    << toString(textBuf, actual);
2248     }
2249     {
2250         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 6));
2251         constexpr float LINE_WIDTH = 100;
2252         // clang-format off
2253         std::vector<LineBreakExpectation> expect = {
2254                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2255                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2256                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2257                 line("\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 100),
2258                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2259         };
2260         // clang-format on
2261 
2262         const auto actual =
2263                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::None, "ja-JP", LINE_WIDTH);
2264         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2265                                                    << " vs " << std::endl
2266                                                    << toString(textBuf, actual);
2267     }
2268 }
2269 
TEST_F(OptimalLineBreakerTest,testPhraseBreakPhrase)2270 TEST_F(OptimalLineBreakerTest, testPhraseBreakPhrase) {
2271     // For short hand of writing expectation for lines.
2272     auto line = [](std::string t, float w) -> LineBreakExpectation {
2273         return {t, w, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, ASCENT, DESCENT};
2274     };
2275 
2276     // Note that disable clang-format everywhere since aligned expectation is more readable.
2277     {
2278         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 1));
2279         constexpr float LINE_WIDTH = 100;
2280         // clang-format off
2281         std::vector<LineBreakExpectation> expect = {
2282                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2283         };
2284         // clang-format on
2285 
2286         const auto actual =
2287                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2288         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2289                                                    << " vs " << std::endl
2290                                                    << toString(textBuf, actual);
2291     }
2292     {
2293         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 2));
2294         constexpr float LINE_WIDTH = 100;
2295         // clang-format off
2296         std::vector<LineBreakExpectation> expect = {
2297                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2298                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2299         };
2300         // clang-format on
2301 
2302         const auto actual =
2303                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2304         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2305                                                    << " vs " << std::endl
2306                                                    << toString(textBuf, actual);
2307     }
2308     {
2309         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 3));
2310         constexpr float LINE_WIDTH = 100;
2311         // clang-format off
2312         std::vector<LineBreakExpectation> expect = {
2313                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2314                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2315                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2316         };
2317         // clang-format on
2318 
2319         const auto actual =
2320                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2321         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2322                                                    << " vs " << std::endl
2323                                                    << toString(textBuf, actual);
2324     }
2325     {
2326         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 4));
2327         constexpr float LINE_WIDTH = 100;
2328         // clang-format off
2329         std::vector<LineBreakExpectation> expect = {
2330                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2331                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2332                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2333                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2334         };
2335         // clang-format on
2336 
2337         const auto actual =
2338                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2339         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2340                                                    << " vs " << std::endl
2341                                                    << toString(textBuf, actual);
2342     }
2343     {
2344         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 5));
2345         constexpr float LINE_WIDTH = 100;
2346         // clang-format off
2347         std::vector<LineBreakExpectation> expect = {
2348                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2349                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2350                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2351                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2352                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2353         };
2354         // clang-format on
2355 
2356         const auto actual =
2357                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2358         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2359                                                    << " vs " << std::endl
2360                                                    << toString(textBuf, actual);
2361     }
2362     {
2363         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 6));
2364         constexpr float LINE_WIDTH = 100;
2365         // clang-format off
2366         std::vector<LineBreakExpectation> expect = {
2367                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2368                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2369                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2370                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2371                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2372                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2373         };
2374         // clang-format on
2375 
2376         const auto actual =
2377                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Phrase, "ja-JP", LINE_WIDTH);
2378         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2379                                                    << " vs " << std::endl
2380                                                    << toString(textBuf, actual);
2381     }
2382 }
2383 
TEST_F(OptimalLineBreakerTest,testPhraseBreakAuto)2384 TEST_F(OptimalLineBreakerTest, testPhraseBreakAuto) {
2385     // For short hand of writing expectation for lines.
2386     auto line = [](std::string t, float w) -> LineBreakExpectation {
2387         return {t, w, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, ASCENT, DESCENT};
2388     };
2389 
2390     // Note that disable clang-format everywhere since aligned expectation is more readable.
2391     {
2392         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 1));
2393         constexpr float LINE_WIDTH = 100;
2394         // clang-format off
2395         std::vector<LineBreakExpectation> expect = {
2396                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2397         };
2398         // clang-format on
2399 
2400         const auto actual =
2401                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2402         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2403                                                    << " vs " << std::endl
2404                                                    << toString(textBuf, actual);
2405     }
2406     {
2407         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 2));
2408         constexpr float LINE_WIDTH = 100;
2409         // clang-format off
2410         std::vector<LineBreakExpectation> expect = {
2411                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2412                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2413         };
2414         // clang-format on
2415 
2416         const auto actual =
2417                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2418         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2419                                                    << " vs " << std::endl
2420                                                    << toString(textBuf, actual);
2421     }
2422     {
2423         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 3));
2424         constexpr float LINE_WIDTH = 100;
2425         // clang-format off
2426         std::vector<LineBreakExpectation> expect = {
2427                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2428                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2429                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2430         };
2431         // clang-format on
2432 
2433         const auto actual =
2434                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2435         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2436                                                    << " vs " << std::endl
2437                                                    << toString(textBuf, actual);
2438     }
2439     {
2440         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 4));
2441         constexpr float LINE_WIDTH = 100;
2442         // clang-format off
2443         std::vector<LineBreakExpectation> expect = {
2444                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2445                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2446                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2447                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 80),
2448         };
2449         // clang-format on
2450 
2451         const auto actual =
2452                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2453         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2454                                                    << " vs " << std::endl
2455                                                    << toString(textBuf, actual);
2456     }
2457     // When the line becomes more or equal to 5, the phrase based line break is disabled.
2458     {
2459         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 5));
2460         constexpr float LINE_WIDTH = 100;
2461         // clang-format off
2462         std::vector<LineBreakExpectation> expect = {
2463                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2464                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2465                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2466                 line("\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 100),
2467         };
2468         // clang-format on
2469 
2470         const auto actual =
2471                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2472         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2473                                                    << " vs " << std::endl
2474                                                    << toString(textBuf, actual);
2475     }
2476     {
2477         const std::vector<uint16_t> textBuf = utf8ToUtf16(repeat(JP_TEXT, 6));
2478         constexpr float LINE_WIDTH = 100;
2479         // clang-format off
2480         std::vector<LineBreakExpectation> expect = {
2481                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5", 100),
2482                 line("\u306F\u6674\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674", 100),
2483                 line("\u5929\u306A\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A", 100),
2484                 line("\u308A\u3002\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", 100),
2485                 line("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002"  , 80),
2486         };
2487         // clang-format on
2488 
2489         const auto actual =
2490                 doLineBreakForJapanese(textBuf, LineBreakWordStyle::Auto, "ja-JP", LINE_WIDTH);
2491         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2492                                                    << " vs " << std::endl
2493                                                    << toString(textBuf, actual);
2494     }
2495 }
2496 
TEST_F(OptimalLineBreakerTest,testBreakLetterSpacing)2497 TEST_F(OptimalLineBreakerTest, testBreakLetterSpacing) {
2498     constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
2499     constexpr HyphenationFrequency NO_HYPHEN = HyphenationFrequency::None;
2500     const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
2501 
2502     constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
2503     constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
2504     // Note that disable clang-format everywhere since aligned expectation is more readable.
2505     {
2506         constexpr float LINE_WIDTH = 1000;
2507         // clang-format off
2508         std::vector<LineBreakExpectation> expect = {
2509                 {"This is an example text.", 470, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2510         };
2511         // clang-format on
2512 
2513         const auto actual =
2514                 doLineBreakWithLetterSpacing(textBuf, HIGH_QUALITY, NO_HYPHEN, 1.0f, LINE_WIDTH);
2515         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2516                                                    << " vs " << std::endl
2517                                                    << toString(textBuf, actual);
2518     }
2519     {
2520         constexpr float LINE_WIDTH = 470;
2521         // clang-format off
2522         std::vector<LineBreakExpectation> expect = {
2523                 {"This is an example text.", 470, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2524         };
2525         // clang-format on
2526 
2527         const auto actual =
2528                 doLineBreakWithLetterSpacing(textBuf, HIGH_QUALITY, NO_HYPHEN, 1.0f, LINE_WIDTH);
2529         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2530                                                    << " vs " << std::endl
2531                                                    << toString(textBuf, actual);
2532     }
2533     {
2534         constexpr float LINE_WIDTH = 460;
2535         // clang-format off
2536         std::vector<LineBreakExpectation> expect = {
2537                 {"This is an example ", 350, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2538                 {"text.",                90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2539         };
2540         // clang-format on
2541 
2542         const auto actual =
2543                 doLineBreakWithLetterSpacing(textBuf, HIGH_QUALITY, NO_HYPHEN, 1.0f, LINE_WIDTH);
2544         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2545                                                    << " vs " << std::endl
2546                                                    << toString(textBuf, actual);
2547     }
2548     {
2549         constexpr float LINE_WIDTH = 240;
2550         // clang-format off
2551         std::vector<LineBreakExpectation> expect = {
2552                 {"This is an ", 190, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2553                 {"example ",    130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2554                 {"text.",        90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2555         };
2556         // clang-format on
2557 
2558         const auto actual =
2559                 doLineBreakWithLetterSpacing(textBuf, HIGH_QUALITY, NO_HYPHEN, 1.0f, LINE_WIDTH);
2560         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2561                                                    << " vs " << std::endl
2562                                                    << toString(textBuf, actual);
2563     }
2564     {
2565         constexpr float LINE_WIDTH = 130;
2566         // clang-format off
2567         std::vector<LineBreakExpectation> expect = {
2568                 {"This ",     70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2569                 {"is an ",    90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2570                 {"example ", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2571                 {"text.",     90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2572         };
2573         // clang-format on
2574 
2575         const auto actual =
2576                 doLineBreakWithLetterSpacing(textBuf, HIGH_QUALITY, NO_HYPHEN, 1.0f, LINE_WIDTH);
2577         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2578                                                    << " vs " << std::endl
2579                                                    << toString(textBuf, actual);
2580     }
2581     {
2582         constexpr float LINE_WIDTH = 120;
2583         // clang-format off
2584         std::vector<LineBreakExpectation> expect = {
2585                 {"This ",  70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2586                 {"is an ", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2587                 {"exa",    50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2588                 {"mple ",  70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2589                 {"text.",  90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2590         };
2591         // clang-format on
2592 
2593         const auto actual =
2594                 doLineBreakWithLetterSpacing(textBuf, HIGH_QUALITY, NO_HYPHEN, 1.0f, LINE_WIDTH);
2595         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2596                                                    << " vs " << std::endl
2597                                                    << toString(textBuf, actual);
2598     }
2599     {
2600         constexpr float LINE_WIDTH = 30;
2601         // clang-format off
2602         std::vector<LineBreakExpectation> expect = {
2603                 {"Th",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2604                 {"is ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2605                 {"is ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2606                 {"an ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2607                 {"e",   10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2608                 {"xa",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2609                 {"mp",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2610                 {"le ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2611                 {"te",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2612                 {"xt",  30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2613                 {".",   10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2614         };
2615         // clang-format on
2616 
2617         const auto actual =
2618                 doLineBreakWithLetterSpacing(textBuf, HIGH_QUALITY, NO_HYPHEN, 1.0f, LINE_WIDTH);
2619         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2620                                                    << " vs " << std::endl
2621                                                    << toString(textBuf, actual);
2622     }
2623     {
2624         constexpr float LINE_WIDTH = 10;
2625         // clang-format off
2626         std::vector<LineBreakExpectation> expect = {
2627                 {"T",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2628                 {"h",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2629                 {"i",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2630                 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2631                 {"i",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2632                 {"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2633                 {"a",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2634                 {"n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2635                 {"e",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2636                 {"x",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2637                 {"a",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2638                 {"m",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2639                 {"p",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2640                 {"l",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2641                 {"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2642                 {"t",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2643                 {"e",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2644                 {"x",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2645                 {"t",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2646                 {".",  10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
2647         };
2648         // clang-format on
2649 
2650         const auto actual =
2651                 doLineBreakWithLetterSpacing(textBuf, HIGH_QUALITY, NO_HYPHEN, 1.0f, LINE_WIDTH);
2652         EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
2653                                                    << " vs " << std::endl
2654                                                    << toString(textBuf, actual);
2655     }
2656 }
2657 
2658 }  // namespace
2659 }  // namespace minikin
2660