1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Unit tests for eliding and formatting utility functions.
7 #include "ui/gfx/text_elider.h"
11 #include "base/files/file_path.h"
12 #include "base/i18n/rtl.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "ui/gfx/font.h"
18 #include "ui/gfx/font_list.h"
19 #include "ui/gfx/font_render_params.h"
20 #include "ui/gfx/text_utils.h"
22 using base::ASCIIToUTF16
;
23 using base::UTF16ToUTF8
;
24 using base::UTF16ToWide
;
25 using base::UTF8ToUTF16
;
26 using base::WideToUTF16
;
33 const std::string input
;
34 const std::string output
;
38 const base::FilePath::StringType input
;
39 const std::string output
;
42 struct UTF16Testcase
{
43 const base::string16 input
;
44 const base::string16 output
;
50 const int compare_result
;
55 // TODO(ios): This test fails on iOS because iOS version of GetStringWidthF
56 // that calls [NSString sizeWithFont] returns the rounded string width.
57 // TODO(338784): Enable this on android.
58 #if defined(OS_IOS) || defined(OS_ANDROID)
59 #define MAYBE_ElideEmail DISABLED_ElideEmail
61 #define MAYBE_ElideEmail ElideEmail
63 TEST(TextEliderTest
, MAYBE_ElideEmail
) {
64 const std::string
kEllipsisStr(kEllipsis
);
66 // Test emails and their expected elided forms (from which the available
67 // widths will be derived).
68 // For elided forms in which both the username and domain must be elided:
69 // the result (how many characters are left on each side) can be font
70 // dependent. To avoid this, the username is prefixed with the characters
71 // expected to remain in the domain.
72 Testcase testcases
[] = {
74 {"g@g.c", kEllipsisStr
},
75 {"ga@co.ca", "ga@c" + kEllipsisStr
+ "a"},
76 {"short@small.com", "s" + kEllipsisStr
+ "@s" + kEllipsisStr
},
77 {"short@small.com", "s" + kEllipsisStr
+ "@small.com"},
78 {"short@longbutlotsofspace.com", "short@longbutlotsofspace.com"},
79 {"short@longbutnotverymuchspace.com",
80 "short@long" + kEllipsisStr
+ ".com"},
81 {"la_short@longbutverytightspace.ca",
82 "la" + kEllipsisStr
+ "@l" + kEllipsisStr
+ "a"},
83 {"longusername@gmail.com", "long" + kEllipsisStr
+ "@gmail.com"},
84 {"elidetothemax@justfits.com", "e" + kEllipsisStr
+ "@justfits.com"},
85 {"thatom_somelongemail@thatdoesntfit.com",
86 "thatom" + kEllipsisStr
+ "@tha" + kEllipsisStr
+ "om"},
87 {"namefits@butthedomaindoesnt.com",
88 "namefits@butthedo" + kEllipsisStr
+ "snt.com"},
89 {"widthtootight@nospace.com", kEllipsisStr
},
90 {"nospaceforusername@l", kEllipsisStr
},
91 {"little@littlespace.com", "l" + kEllipsisStr
+ "@l" + kEllipsisStr
},
92 {"l@llllllllllllllllllllllll.com", "l@lllll" + kEllipsisStr
+ ".com"},
93 {"messed\"up@whyanat\"++@notgoogley.com",
94 "messed\"up@whyanat\"++@notgoogley.com"},
95 {"messed\"up@whyanat\"++@notgoogley.com",
96 "messed\"up@why" + kEllipsisStr
+ "@notgoogley.com"},
97 {"noca_messed\"up@whyanat\"++@notgoogley.ca",
98 "noca" + kEllipsisStr
+ "@no" + kEllipsisStr
+ "ca"},
99 {"at\"@@@@@@@@@...@@.@.@.@@@\"@madness.com",
100 "at\"@@@@@@@@@...@@.@." + kEllipsisStr
+ "@madness.com"},
101 // Special case: "m..." takes more than half of the available width; thus
102 // the domain must elide to "l..." and not "l...l" as it must allow enough
103 // space for the minimal username elision although its half of the
104 // available width would normally allow it to elide to "l...l".
105 {"mmmmm@llllllllll", "m" + kEllipsisStr
+ "@l" + kEllipsisStr
},
108 const FontList font_list
;
109 for (size_t i
= 0; i
< arraysize(testcases
); ++i
) {
110 const base::string16 expected_output
= UTF8ToUTF16(testcases
[i
].output
);
111 EXPECT_EQ(expected_output
,
112 ElideText(UTF8ToUTF16(testcases
[i
].input
), font_list
,
113 GetStringWidthF(expected_output
, font_list
),
118 // TODO(338784): Enable this on android.
119 #if defined(OS_ANDROID)
120 #define MAYBE_ElideEmailMoreSpace DISABLED_ElideEmailMoreSpace
122 #define MAYBE_ElideEmailMoreSpace ElideEmailMoreSpace
124 TEST(TextEliderTest
, MAYBE_ElideEmailMoreSpace
) {
125 const int test_width_factors
[] = {
130 const std::string test_emails
[] = {
133 "short@verysuperdupperlongdomain.com",
134 "supermegalongusername@withasuperlonnnggggdomain.gouv.qc.ca",
137 const FontList font_list
;
138 for (size_t i
= 0; i
< arraysize(test_width_factors
); ++i
) {
139 const int test_width
=
140 font_list
.GetExpectedTextWidth(test_width_factors
[i
]);
141 for (size_t j
= 0; j
< arraysize(test_emails
); ++j
) {
142 // Extra space is available: the email should not be elided.
143 const base::string16 test_email
= UTF8ToUTF16(test_emails
[j
]);
144 EXPECT_EQ(test_email
,
145 ElideText(test_email
, font_list
, test_width
, ELIDE_EMAIL
));
150 // TODO(ios): This test fails on iOS because iOS version of GetStringWidthF
151 // that calls [NSString sizeWithFont] returns the rounded string width.
152 // TODO(338784): Enable this on android.
153 #if defined(OS_IOS) || defined(OS_ANDROID)
154 #define MAYBE_TestFilenameEliding DISABLED_TestFilenameEliding
156 #define MAYBE_TestFilenameEliding TestFilenameEliding
158 TEST(TextEliderTest
, MAYBE_TestFilenameEliding
) {
159 const std::string
kEllipsisStr(kEllipsis
);
160 const base::FilePath::StringType kPathSeparator
=
161 base::FilePath::StringType().append(1, base::FilePath::kSeparators
[0]);
163 FileTestcase testcases
[] = {
164 {FILE_PATH_LITERAL(""), ""},
165 {FILE_PATH_LITERAL("."), "."},
166 {FILE_PATH_LITERAL("filename.exe"), "filename.exe"},
167 {FILE_PATH_LITERAL(".longext"), ".longext"},
168 {FILE_PATH_LITERAL("pie"), "pie"},
169 {FILE_PATH_LITERAL("c:") + kPathSeparator
+ FILE_PATH_LITERAL("path") +
170 kPathSeparator
+ FILE_PATH_LITERAL("filename.pie"),
172 {FILE_PATH_LITERAL("c:") + kPathSeparator
+ FILE_PATH_LITERAL("path") +
173 kPathSeparator
+ FILE_PATH_LITERAL("longfilename.pie"),
174 "long" + kEllipsisStr
+ ".pie"},
175 {FILE_PATH_LITERAL("http://path.com/filename.pie"), "filename.pie"},
176 {FILE_PATH_LITERAL("http://path.com/longfilename.pie"),
177 "long" + kEllipsisStr
+ ".pie"},
178 {FILE_PATH_LITERAL("piesmashingtacularpants"), "pie" + kEllipsisStr
},
179 {FILE_PATH_LITERAL(".piesmashingtacularpants"), ".pie" + kEllipsisStr
},
180 {FILE_PATH_LITERAL("cheese."), "cheese."},
181 {FILE_PATH_LITERAL("file name.longext"),
182 "file" + kEllipsisStr
+ ".longext"},
183 {FILE_PATH_LITERAL("fil ename.longext"),
184 "fil " + kEllipsisStr
+ ".longext"},
185 {FILE_PATH_LITERAL("filename.longext"),
186 "file" + kEllipsisStr
+ ".longext"},
187 {FILE_PATH_LITERAL("filename.middleext.longext"),
188 "filename.mid" + kEllipsisStr
+ ".longext"},
189 {FILE_PATH_LITERAL("filename.superduperextremelylongext"),
190 "filename.sup" + kEllipsisStr
+ "emelylongext"},
191 {FILE_PATH_LITERAL("filenamereallylongtext.superduperextremelylongext"),
192 "filenamereall" + kEllipsisStr
+ "emelylongext"},
193 {FILE_PATH_LITERAL("file.name.really.long.text.superduperextremelylongext"),
194 "file.name.re" + kEllipsisStr
+ "emelylongext"}
197 static const FontList font_list
;
198 for (size_t i
= 0; i
< arraysize(testcases
); ++i
) {
199 base::FilePath
filepath(testcases
[i
].input
);
200 base::string16 expected
= UTF8ToUTF16(testcases
[i
].output
);
201 expected
= base::i18n::GetDisplayStringInLTRDirectionality(expected
);
202 EXPECT_EQ(expected
, ElideFilename(filepath
, font_list
,
203 GetStringWidthF(UTF8ToUTF16(testcases
[i
].output
), font_list
)));
207 // TODO(338784): Enable this on android.
208 #if defined(OS_ANDROID)
209 #define MAYBE_ElideTextTruncate DISABLED_ElideTextTruncate
211 #define MAYBE_ElideTextTruncate ElideTextTruncate
213 TEST(TextEliderTest
, MAYBE_ElideTextTruncate
) {
214 const FontList font_list
;
215 const float kTestWidth
= GetStringWidthF(ASCIIToUTF16("Test"), font_list
);
223 { "", kTestWidth
, "" },
224 { "Tes", kTestWidth
, "Tes" },
225 { "Test", kTestWidth
, "Test" },
226 { "Tests", kTestWidth
, "Test" },
229 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
230 base::string16 result
= ElideText(UTF8ToUTF16(cases
[i
].input
), font_list
,
231 cases
[i
].width
, TRUNCATE
);
232 EXPECT_EQ(cases
[i
].output
, UTF16ToUTF8(result
));
236 // TODO(338784): Enable this on android.
237 #if defined(OS_ANDROID)
238 #define MAYBE_ElideTextEllipsis DISABLED_ElideTextEllipsis
240 #define MAYBE_ElideTextEllipsis ElideTextEllipsis
242 TEST(TextEliderTest
, MAYBE_ElideTextEllipsis
) {
243 const FontList font_list
;
244 const float kTestWidth
= GetStringWidthF(ASCIIToUTF16("Test"), font_list
);
245 const char* kEllipsis
= "\xE2\x80\xA6";
246 const float kEllipsisWidth
=
247 GetStringWidthF(UTF8ToUTF16(kEllipsis
), font_list
);
255 { "Test", kEllipsisWidth
, kEllipsis
},
256 { "", kTestWidth
, "" },
257 { "Tes", kTestWidth
, "Tes" },
258 { "Test", kTestWidth
, "Test" },
261 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
262 base::string16 result
= ElideText(UTF8ToUTF16(cases
[i
].input
), font_list
,
263 cases
[i
].width
, ELIDE_TAIL
);
264 EXPECT_EQ(cases
[i
].output
, UTF16ToUTF8(result
));
268 // TODO(338784): Enable this on android.
269 #if defined(OS_ANDROID)
270 #define MAYBE_ElideTextEllipsisFront DISABLED_ElideTextEllipsisFront
272 #define MAYBE_ElideTextEllipsisFront ElideTextEllipsisFront
274 TEST(TextEliderTest
, MAYBE_ElideTextEllipsisFront
) {
275 const FontList font_list
;
276 const float kTestWidth
= GetStringWidthF(ASCIIToUTF16("Test"), font_list
);
277 const std::string
kEllipsisStr(kEllipsis
);
278 const float kEllipsisWidth
=
279 GetStringWidthF(UTF8ToUTF16(kEllipsis
), font_list
);
280 const float kEllipsis23Width
=
281 GetStringWidthF(UTF8ToUTF16(kEllipsisStr
+ "23"), font_list
);
285 const base::string16 output
;
287 { "", 0, base::string16() },
288 { "Test", 0, base::string16() },
289 { "Test", kEllipsisWidth
, UTF8ToUTF16(kEllipsisStr
) },
290 { "", kTestWidth
, base::string16() },
291 { "Tes", kTestWidth
, ASCIIToUTF16("Tes") },
292 { "Test", kTestWidth
, ASCIIToUTF16("Test") },
293 { "Test123", kEllipsis23Width
, UTF8ToUTF16(kEllipsisStr
+ "23") },
296 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
297 base::string16 result
= ElideText(UTF8ToUTF16(cases
[i
].input
), font_list
,
298 cases
[i
].width
, ELIDE_HEAD
);
299 EXPECT_EQ(cases
[i
].output
, result
);
303 // Checks that all occurrences of |first_char| are followed by |second_char| and
304 // all occurrences of |second_char| are preceded by |first_char| in |text|. Can
305 // be used to test surrogate pairs or two-character combining sequences.
306 static void CheckCodeUnitPairs(const base::string16
& text
,
307 base::char16 first_char
,
308 base::char16 second_char
) {
309 for (size_t index
= 0; index
< text
.length(); ++index
) {
310 EXPECT_NE(second_char
, text
[index
]);
311 if (text
[index
] == first_char
) {
312 ASSERT_LT(++index
, text
.length());
313 EXPECT_EQ(second_char
, text
[index
]);
318 // Test that both both UTF-16 surrogate pairs and combining character sequences
319 // do not get split by ElideText.
320 // TODO(338784): Enable this on android.
321 #if defined(OS_ANDROID)
322 #define MAYBE_ElideTextAtomicSequences DISABLED_ElideTextAtomicSequences
324 #define MAYBE_ElideTextAtomicSequences ElideTextAtomicSequences
326 TEST(TextEliderTest
, MAYBE_ElideTextAtomicSequences
) {
327 const FontList font_list
;
328 // The below is 'MUSICAL SYMBOL G CLEF' (U+1D11E), which is represented in
329 // UTF-16 as two code units forming a surrogate pair: 0xD834 0xDD1E.
330 const base::char16 kSurrogate
[] = {0xD834, 0xDD1E, 0};
331 // The below is a Devanagari two-character combining sequence U+0921 U+093F.
332 // The sequence forms a single display character and should not be separated.
333 const base::char16 kCombiningSequence
[] = {0x921, 0x93F, 0};
334 std::vector
<base::string16
> pairs
;
335 pairs
.push_back(kSurrogate
);
336 pairs
.push_back(kCombiningSequence
);
338 for (const base::string16
& pair
: pairs
) {
339 base::char16 first_char
= pair
[0];
340 base::char16 second_char
= pair
[1];
341 base::string16 test_string
= pair
+ UTF8ToUTF16("x") + pair
;
342 SCOPED_TRACE(test_string
);
343 const float test_string_width
= GetStringWidthF(test_string
, font_list
);
344 base::string16 result
;
346 // Elide |text_string| to all possible widths and check that no instance of
347 // |pair| was split in two.
348 for (float width
= 0; width
<= test_string_width
; width
++) {
349 result
= ElideText(test_string
, font_list
, width
, TRUNCATE
);
350 CheckCodeUnitPairs(result
, first_char
, second_char
);
352 result
= ElideText(test_string
, font_list
, width
, ELIDE_TAIL
);
353 CheckCodeUnitPairs(result
, first_char
, second_char
);
355 result
= ElideText(test_string
, font_list
, width
, ELIDE_MIDDLE
);
356 CheckCodeUnitPairs(result
, first_char
, second_char
);
358 result
= ElideText(test_string
, font_list
, width
, ELIDE_HEAD
);
359 CheckCodeUnitPairs(result
, first_char
, second_char
);
364 // TODO(338784): Enable this on android.
365 #if defined(OS_ANDROID)
366 #define MAYBE_ElideTextLongStrings DISABLED_ElideTextLongStrings
368 #define MAYBE_ElideTextLongStrings ElideTextLongStrings
370 TEST(TextEliderTest
, MAYBE_ElideTextLongStrings
) {
371 const base::string16 kEllipsisStr
= UTF8ToUTF16(kEllipsis
);
372 base::string16
data_scheme(UTF8ToUTF16("data:text/plain,"));
373 size_t data_scheme_length
= data_scheme
.length();
375 base::string16
ten_a(10, 'a');
376 base::string16
hundred_a(100, 'a');
377 base::string16
thousand_a(1000, 'a');
378 base::string16
ten_thousand_a(10000, 'a');
379 base::string16
hundred_thousand_a(100000, 'a');
380 base::string16
million_a(1000000, 'a');
382 // TODO(gbillock): Improve these tests by adding more string diversity and
383 // doing string compares instead of length compares. See bug 338836.
385 size_t number_of_as
= 156;
386 base::string16
long_string_end(
387 data_scheme
+ base::string16(number_of_as
, 'a') + kEllipsisStr
);
388 UTF16Testcase testcases_end
[] = {
389 { data_scheme
+ ten_a
, data_scheme
+ ten_a
},
390 { data_scheme
+ hundred_a
, data_scheme
+ hundred_a
},
391 { data_scheme
+ thousand_a
, long_string_end
},
392 { data_scheme
+ ten_thousand_a
, long_string_end
},
393 { data_scheme
+ hundred_thousand_a
, long_string_end
},
394 { data_scheme
+ million_a
, long_string_end
},
397 const FontList font_list
;
398 float ellipsis_width
= GetStringWidthF(kEllipsisStr
, font_list
);
399 for (size_t i
= 0; i
< arraysize(testcases_end
); ++i
) {
400 // Compare sizes rather than actual contents because if the test fails,
401 // output is rather long.
402 EXPECT_EQ(testcases_end
[i
].output
.size(),
403 ElideText(testcases_end
[i
].input
, font_list
,
404 GetStringWidthF(testcases_end
[i
].output
, font_list
),
406 EXPECT_EQ(kEllipsisStr
,
407 ElideText(testcases_end
[i
].input
, font_list
, ellipsis_width
,
411 size_t number_of_trailing_as
= (data_scheme_length
+ number_of_as
) / 2;
412 base::string16
long_string_middle(data_scheme
+
413 base::string16(number_of_as
- number_of_trailing_as
, 'a') + kEllipsisStr
+
414 base::string16(number_of_trailing_as
, 'a'));
415 UTF16Testcase testcases_middle
[] = {
416 { data_scheme
+ ten_a
, data_scheme
+ ten_a
},
417 { data_scheme
+ hundred_a
, data_scheme
+ hundred_a
},
418 { data_scheme
+ thousand_a
, long_string_middle
},
419 { data_scheme
+ ten_thousand_a
, long_string_middle
},
420 { data_scheme
+ hundred_thousand_a
, long_string_middle
},
421 { data_scheme
+ million_a
, long_string_middle
},
424 for (size_t i
= 0; i
< arraysize(testcases_middle
); ++i
) {
425 // Compare sizes rather than actual contents because if the test fails,
426 // output is rather long.
427 EXPECT_EQ(testcases_middle
[i
].output
.size(),
428 ElideText(testcases_middle
[i
].input
, font_list
,
429 GetStringWidthF(testcases_middle
[i
].output
, font_list
),
430 ELIDE_MIDDLE
).size());
431 EXPECT_EQ(kEllipsisStr
,
432 ElideText(testcases_middle
[i
].input
, font_list
, ellipsis_width
,
436 base::string16
long_string_beginning(
437 kEllipsisStr
+ base::string16(number_of_as
, 'a'));
438 UTF16Testcase testcases_beginning
[] = {
439 { data_scheme
+ ten_a
, data_scheme
+ ten_a
},
440 { data_scheme
+ hundred_a
, data_scheme
+ hundred_a
},
441 { data_scheme
+ thousand_a
, long_string_beginning
},
442 { data_scheme
+ ten_thousand_a
, long_string_beginning
},
443 { data_scheme
+ hundred_thousand_a
, long_string_beginning
},
444 { data_scheme
+ million_a
, long_string_beginning
},
446 for (size_t i
= 0; i
< arraysize(testcases_beginning
); ++i
) {
447 EXPECT_EQ(testcases_beginning
[i
].output
.size(),
449 testcases_beginning
[i
].input
, font_list
,
450 GetStringWidthF(testcases_beginning
[i
].output
, font_list
),
452 EXPECT_EQ(kEllipsisStr
,
453 ElideText(testcases_beginning
[i
].input
, font_list
, ellipsis_width
,
458 // Detailed tests for StringSlicer. These are faster and test more of the edge
459 // cases than the above tests which are more end-to-end.
461 TEST(TextEliderTest
, StringSlicerBasicTest
) {
462 // Must store strings in variables (StringSlicer retains a reference to them).
463 base::string16
text(UTF8ToUTF16("Hello, world!"));
464 base::string16
ellipsis(kEllipsisUTF16
);
465 StringSlicer
slicer(text
, ellipsis
, false, false);
467 EXPECT_EQ(UTF8ToUTF16(""), slicer
.CutString(0, false));
468 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(0, true));
470 EXPECT_EQ(UTF8ToUTF16("Hell"), slicer
.CutString(4, false));
471 EXPECT_EQ(UTF8ToUTF16("Hell") + kEllipsisUTF16
, slicer
.CutString(4, true));
473 EXPECT_EQ(text
, slicer
.CutString(text
.length(), false));
474 EXPECT_EQ(text
+ kEllipsisUTF16
, slicer
.CutString(text
.length(), true));
476 StringSlicer
slicer_begin(text
, ellipsis
, false, true);
477 EXPECT_EQ(UTF8ToUTF16("rld!"), slicer_begin
.CutString(4, false));
478 EXPECT_EQ(kEllipsisUTF16
+ UTF8ToUTF16("rld!"),
479 slicer_begin
.CutString(4, true));
481 StringSlicer
slicer_mid(text
, ellipsis
, true, false);
482 EXPECT_EQ(UTF8ToUTF16("Held!"), slicer_mid
.CutString(5, false));
483 EXPECT_EQ(UTF8ToUTF16("Hel") + kEllipsisUTF16
+ UTF8ToUTF16("d!"),
484 slicer_mid
.CutString(5, true));
487 TEST(TextEliderTest
, StringSlicerSurrogate
) {
488 // The below is 'MUSICAL SYMBOL G CLEF' (U+1D11E), which is represented in
489 // UTF-16 as two code units forming a surrogate pair: 0xD834 0xDD1E.
490 const base::char16 kSurrogate
[] = {0xD834, 0xDD1E, 0};
491 base::string16
text(UTF8ToUTF16("abc") + kSurrogate
+ UTF8ToUTF16("xyz"));
492 base::string16
ellipsis(kEllipsisUTF16
);
493 StringSlicer
slicer(text
, ellipsis
, false, false);
495 // Cut surrogate on the right. Should round left and exclude the surrogate.
496 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(0, true));
497 EXPECT_EQ(UTF8ToUTF16("abc") + kEllipsisUTF16
, slicer
.CutString(4, true));
498 EXPECT_EQ(text
+ kEllipsisUTF16
, slicer
.CutString(text
.length(), true));
500 // Cut surrogate on the left. Should round left and include the surrogate.
501 StringSlicer
slicer_begin(text
, ellipsis
, false, true);
502 EXPECT_EQ(base::string16(kEllipsisUTF16
) + kSurrogate
+ UTF8ToUTF16("xyz"),
503 slicer_begin
.CutString(4, true));
505 // Cut surrogate in the middle. Should round right and exclude the surrogate.
506 base::string16
short_text(UTF8ToUTF16("abc") + kSurrogate
);
507 StringSlicer
slicer_mid(short_text
, ellipsis
, true, false);
508 EXPECT_EQ(UTF8ToUTF16("a") + kEllipsisUTF16
, slicer_mid
.CutString(2, true));
510 // String that starts with a dangling trailing surrogate.
511 base::char16 dangling_trailing_chars
[] = {kSurrogate
[1], 0};
512 base::string16
dangling_trailing_text(dangling_trailing_chars
);
513 StringSlicer
slicer_dangling_trailing(dangling_trailing_text
, ellipsis
, false,
515 EXPECT_EQ(base::string16(kEllipsisUTF16
),
516 slicer_dangling_trailing
.CutString(0, true));
517 EXPECT_EQ(dangling_trailing_text
+ kEllipsisUTF16
,
518 slicer_dangling_trailing
.CutString(1, true));
521 TEST(TextEliderTest
, StringSlicerCombining
) {
522 // The following string contains three combining character sequences (one for
523 // each category of combining mark):
524 // LATIN SMALL LETTER E + COMBINING ACUTE ACCENT + COMBINING CEDILLA
525 // LATIN SMALL LETTER X + COMBINING ENCLOSING KEYCAP
526 // DEVANAGARI LETTER DDA + DEVANAGARI VOWEL SIGN I
527 const base::char16 kText
[] = {
528 'e', 0x301, 0x327, ' ', 'x', 0x20E3, ' ', 0x921, 0x93F, 0};
529 base::string16
text(kText
);
530 base::string16
ellipsis(kEllipsisUTF16
);
531 StringSlicer
slicer(text
, ellipsis
, false, false);
533 // Attempt to cut the string for all lengths. When a combining sequence is
534 // cut, it should always round left and exclude the combining sequence.
536 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(0, true));
537 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(1, true));
538 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(2, true));
539 EXPECT_EQ(text
.substr(0, 3) + kEllipsisUTF16
, slicer
.CutString(3, true));
541 EXPECT_EQ(text
.substr(0, 4) + kEllipsisUTF16
, slicer
.CutString(4, true));
542 EXPECT_EQ(text
.substr(0, 4) + kEllipsisUTF16
, slicer
.CutString(5, true));
543 EXPECT_EQ(text
.substr(0, 6) + kEllipsisUTF16
, slicer
.CutString(6, true));
545 EXPECT_EQ(text
.substr(0, 7) + kEllipsisUTF16
, slicer
.CutString(7, true));
546 EXPECT_EQ(text
.substr(0, 7) + kEllipsisUTF16
, slicer
.CutString(8, true));
547 EXPECT_EQ(text
+ kEllipsisUTF16
, slicer
.CutString(9, true));
549 // Cut string in the middle, splitting the second sequence in half. Should
550 // round both left and right, excluding the second sequence.
551 StringSlicer
slicer_mid(text
, ellipsis
, true, false);
552 EXPECT_EQ(text
.substr(0, 4) + kEllipsisUTF16
+ text
.substr(6),
553 slicer_mid
.CutString(9, true));
555 // String that starts with a dangling combining mark.
556 base::char16 dangling_mark_chars
[] = {text
[1], 0};
557 base::string16
dangling_mark_text(dangling_mark_chars
);
558 StringSlicer
slicer_dangling_mark(dangling_mark_text
, ellipsis
, false, false);
559 EXPECT_EQ(base::string16(kEllipsisUTF16
),
560 slicer_dangling_mark
.CutString(0, true));
561 EXPECT_EQ(dangling_mark_text
+ kEllipsisUTF16
,
562 slicer_dangling_mark
.CutString(1, true));
565 TEST(TextEliderTest
, StringSlicerCombiningSurrogate
) {
566 // The ultimate test: combining sequences comprised of surrogate pairs.
567 // The following string contains a single combining character sequence:
568 // MUSICAL SYMBOL G CLEF (U+1D11E) + MUSICAL SYMBOL COMBINING FLAG-1 (U+1D16E)
569 // Represented as four UTF-16 code units.
570 const base::char16 kText
[] = {0xD834, 0xDD1E, 0xD834, 0xDD6E, 0};
571 base::string16
text(kText
);
572 base::string16
ellipsis(kEllipsisUTF16
);
573 StringSlicer
slicer(text
, ellipsis
, false, false);
575 // Attempt to cut the string for all lengths. Should always round left and
576 // exclude the combining sequence.
577 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(0, true));
578 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(1, true));
579 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(2, true));
580 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer
.CutString(3, true));
581 EXPECT_EQ(text
+ kEllipsisUTF16
, slicer
.CutString(4, true));
583 // Cut string in the middle. Should exclude the sequence.
584 StringSlicer
slicer_mid(text
, ellipsis
, true, false);
585 EXPECT_EQ(base::string16(kEllipsisUTF16
), slicer_mid
.CutString(4, true));
588 TEST(TextEliderTest
, ElideString
) {
595 { "Hello", 0, true, "" },
596 { "", 0, false, "" },
597 { "Hello, my name is Tom", 1, true, "H" },
598 { "Hello, my name is Tom", 2, true, "He" },
599 { "Hello, my name is Tom", 3, true, "H.m" },
600 { "Hello, my name is Tom", 4, true, "H..m" },
601 { "Hello, my name is Tom", 5, true, "H...m" },
602 { "Hello, my name is Tom", 6, true, "He...m" },
603 { "Hello, my name is Tom", 7, true, "He...om" },
604 { "Hello, my name is Tom", 10, true, "Hell...Tom" },
605 { "Hello, my name is Tom", 100, false, "Hello, my name is Tom" }
607 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
608 base::string16 output
;
609 EXPECT_EQ(cases
[i
].result
,
610 ElideString(UTF8ToUTF16(cases
[i
].input
),
611 cases
[i
].max_len
, &output
));
612 EXPECT_EQ(cases
[i
].output
, UTF16ToUTF8(output
));
616 // TODO(338784): Enable this on android.
617 #if defined(OS_ANDROID)
618 #define MAYBE_ElideRectangleText DISABLED_ElideRectangleText
620 #define MAYBE_ElideRectangleText ElideRectangleText
622 TEST(TextEliderTest
, MAYBE_ElideRectangleText
) {
623 const FontList font_list
;
624 const int line_height
= font_list
.GetHeight();
625 const float test_width
= GetStringWidthF(ASCIIToUTF16("Test"), font_list
);
629 float available_pixel_width
;
630 int available_pixel_height
;
634 { "", 0, 0, false, NULL
},
635 { "", 1, 1, false, NULL
},
636 { "Test", test_width
, 0, true, NULL
},
637 { "Test", test_width
, 1, false, "Test" },
638 { "Test", test_width
, line_height
, false, "Test" },
639 { "Test Test", test_width
, line_height
, true, "Test" },
640 { "Test Test", test_width
, line_height
+ 1, false, "Test|Test" },
641 { "Test Test", test_width
, line_height
* 2, false, "Test|Test" },
642 { "Test Test", test_width
, line_height
* 3, false, "Test|Test" },
643 { "Test Test", test_width
* 2, line_height
* 2, false, "Test|Test" },
644 { "Test Test", test_width
* 3, line_height
, false, "Test Test" },
645 { "Test\nTest", test_width
* 3, line_height
* 2, false, "Test|Test" },
646 { "Te\nst Te", test_width
, line_height
* 3, false, "Te|st|Te" },
647 { "\nTest", test_width
, line_height
* 2, false, "|Test" },
648 { "\nTest", test_width
, line_height
, true, "" },
649 { "\n\nTest", test_width
, line_height
* 3, false, "||Test" },
650 { "\n\nTest", test_width
, line_height
* 2, true, "|" },
651 { "Test\n", 2 * test_width
, line_height
* 5, false, "Test|" },
652 { "Test\n\n", 2 * test_width
, line_height
* 5, false, "Test||" },
653 { "Test\n\n\n", 2 * test_width
, line_height
* 5, false, "Test|||" },
654 { "Test\nTest\n\n", 2 * test_width
, line_height
* 5, false, "Test|Test||" },
655 { "Test\n\nTest\n", 2 * test_width
, line_height
* 5, false, "Test||Test|" },
656 { "Test\n\n\nTest", 2 * test_width
, line_height
* 5, false, "Test|||Test" },
657 { "Te ", test_width
, line_height
, false, "Te" },
658 { "Te Te Test", test_width
, 3 * line_height
, false, "Te|Te|Test" },
661 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
662 std::vector
<base::string16
> lines
;
663 EXPECT_EQ(cases
[i
].truncated_y
? INSUFFICIENT_SPACE_VERTICAL
: 0,
664 ElideRectangleText(UTF8ToUTF16(cases
[i
].input
),
666 cases
[i
].available_pixel_width
,
667 cases
[i
].available_pixel_height
,
670 if (cases
[i
].output
) {
671 const std::string result
=
672 UTF16ToUTF8(base::JoinString(lines
, ASCIIToUTF16("|")));
673 EXPECT_EQ(cases
[i
].output
, result
) << "Case " << i
<< " failed!";
675 EXPECT_TRUE(lines
.empty()) << "Case " << i
<< " failed!";
680 // TODO(338784): Enable this on android.
681 #if defined(OS_ANDROID)
682 #define MAYBE_ElideRectangleTextPunctuation \
683 DISABLED_ElideRectangleTextPunctuation
685 #define MAYBE_ElideRectangleTextPunctuation ElideRectangleTextPunctuation
687 TEST(TextEliderTest
, MAYBE_ElideRectangleTextPunctuation
) {
688 const FontList font_list
;
689 const int line_height
= font_list
.GetHeight();
690 const float test_width
= GetStringWidthF(ASCIIToUTF16("Test"), font_list
);
691 const float test_t_width
= GetStringWidthF(ASCIIToUTF16("Test T"), font_list
);
695 float available_pixel_width
;
696 int available_pixel_height
;
701 { "Test T.", test_t_width
, line_height
* 2, false, false, "Test|T." },
702 { "Test T ?", test_t_width
, line_height
* 2, false, false, "Test|T ?" },
703 { "Test. Test", test_width
, line_height
* 3, false, true, "Test|Test" },
704 { "Test. Test", test_width
, line_height
* 3, true, false, "Test|.|Test" },
707 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
708 std::vector
<base::string16
> lines
;
709 const WordWrapBehavior wrap_behavior
=
710 (cases
[i
].wrap_words
? WRAP_LONG_WORDS
: TRUNCATE_LONG_WORDS
);
711 EXPECT_EQ(cases
[i
].truncated_x
? INSUFFICIENT_SPACE_HORIZONTAL
: 0,
712 ElideRectangleText(UTF8ToUTF16(cases
[i
].input
),
714 cases
[i
].available_pixel_width
,
715 cases
[i
].available_pixel_height
,
718 if (cases
[i
].output
) {
719 const std::string result
=
720 UTF16ToUTF8(base::JoinString(lines
, base::ASCIIToUTF16("|")));
721 EXPECT_EQ(cases
[i
].output
, result
) << "Case " << i
<< " failed!";
723 EXPECT_TRUE(lines
.empty()) << "Case " << i
<< " failed!";
728 // TODO(338784): Enable this on android.
729 #if defined(OS_ANDROID)
730 #define MAYBE_ElideRectangleTextLongWords DISABLED_ElideRectangleTextLongWords
732 #define MAYBE_ElideRectangleTextLongWords ElideRectangleTextLongWords
734 TEST(TextEliderTest
, MAYBE_ElideRectangleTextLongWords
) {
735 const FontList font_list
;
736 const int kAvailableHeight
= 1000;
737 const base::string16 kElidedTesting
=
738 UTF8ToUTF16(std::string("Tes") + kEllipsis
);
739 const float elided_width
= GetStringWidthF(kElidedTesting
, font_list
);
740 const float test_width
= GetStringWidthF(ASCIIToUTF16("Test"), font_list
);
744 float available_pixel_width
;
745 WordWrapBehavior wrap_behavior
;
749 { "Testing", test_width
, IGNORE_LONG_WORDS
, false, "Testing" },
750 { "X Testing", test_width
, IGNORE_LONG_WORDS
, false, "X|Testing" },
751 { "Test Testing", test_width
, IGNORE_LONG_WORDS
, false, "Test|Testing" },
752 { "Test\nTesting", test_width
, IGNORE_LONG_WORDS
, false, "Test|Testing" },
753 { "Test Tests ", test_width
, IGNORE_LONG_WORDS
, false, "Test|Tests" },
754 { "Test Tests T", test_width
, IGNORE_LONG_WORDS
, false, "Test|Tests|T" },
756 { "Testing", elided_width
, ELIDE_LONG_WORDS
, true, "Tes..." },
757 { "X Testing", elided_width
, ELIDE_LONG_WORDS
, true, "X|Tes..." },
758 { "Test Testing", elided_width
, ELIDE_LONG_WORDS
, true, "Test|Tes..." },
759 { "Test\nTesting", elided_width
, ELIDE_LONG_WORDS
, true, "Test|Tes..." },
761 { "Testing", test_width
, TRUNCATE_LONG_WORDS
, true, "Test" },
762 { "X Testing", test_width
, TRUNCATE_LONG_WORDS
, true, "X|Test" },
763 { "Test Testing", test_width
, TRUNCATE_LONG_WORDS
, true, "Test|Test" },
764 { "Test\nTesting", test_width
, TRUNCATE_LONG_WORDS
, true, "Test|Test" },
765 { "Test Tests ", test_width
, TRUNCATE_LONG_WORDS
, true, "Test|Test" },
766 { "Test Tests T", test_width
, TRUNCATE_LONG_WORDS
, true, "Test|Test|T" },
768 { "Testing", test_width
, WRAP_LONG_WORDS
, false, "Test|ing" },
769 { "X Testing", test_width
, WRAP_LONG_WORDS
, false, "X|Test|ing" },
770 { "Test Testing", test_width
, WRAP_LONG_WORDS
, false, "Test|Test|ing" },
771 { "Test\nTesting", test_width
, WRAP_LONG_WORDS
, false, "Test|Test|ing" },
772 { "Test Tests ", test_width
, WRAP_LONG_WORDS
, false, "Test|Test|s" },
773 { "Test Tests T", test_width
, WRAP_LONG_WORDS
, false, "Test|Test|s T" },
774 { "TestTestTest", test_width
, WRAP_LONG_WORDS
, false, "Test|Test|Test" },
775 { "TestTestTestT", test_width
, WRAP_LONG_WORDS
, false, "Test|Test|Test|T" },
778 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
779 std::vector
<base::string16
> lines
;
780 EXPECT_EQ(cases
[i
].truncated_x
? INSUFFICIENT_SPACE_HORIZONTAL
: 0,
781 ElideRectangleText(UTF8ToUTF16(cases
[i
].input
),
783 cases
[i
].available_pixel_width
,
785 cases
[i
].wrap_behavior
,
787 std::string
expected_output(cases
[i
].output
);
788 base::ReplaceSubstringsAfterOffset(&expected_output
, 0, "...", kEllipsis
);
789 const std::string result
=
790 UTF16ToUTF8(base::JoinString(lines
, base::ASCIIToUTF16("|")));
791 EXPECT_EQ(expected_output
, result
) << "Case " << i
<< " failed!";
795 // This test is to make sure that the width of each wrapped line does not
796 // exceed the available width. On some platform like Mac, this test used to
797 // fail because the truncated integer width is returned for the string
798 // and the accumulation of the truncated values causes the elide function
799 // to wrap incorrectly.
800 // TODO(338784): Enable this on android.
801 #if defined(OS_ANDROID)
802 #define MAYBE_ElideRectangleTextCheckLineWidth \
803 DISABLED_ElideRectangleTextCheckLineWidth
805 #define MAYBE_ElideRectangleTextCheckLineWidth ElideRectangleTextCheckLineWidth
807 TEST(TextEliderTest
, MAYBE_ElideRectangleTextCheckLineWidth
) {
809 #if defined(OS_MACOSX) && !defined(OS_IOS)
810 // Use a specific font to expose the line width exceeding problem.
811 font_list
= FontList(Font("LucidaGrande", 12));
813 const float kAvailableWidth
= 235;
814 const int kAvailableHeight
= 1000;
815 const char text
[] = "that Russian place we used to go to after fencing";
816 std::vector
<base::string16
> lines
;
817 EXPECT_EQ(0, ElideRectangleText(UTF8ToUTF16(text
),
823 ASSERT_EQ(2u, lines
.size());
824 EXPECT_LE(GetStringWidthF(lines
[0], font_list
), kAvailableWidth
);
825 EXPECT_LE(GetStringWidthF(lines
[1], font_list
), kAvailableWidth
);
829 // This test was created specifically to test a message from crbug.com/415213.
830 // It tests that width of concatenation of words equals sum of widths of the
832 TEST(TextEliderTest
, ElideRectangleTextCheckConcatWidthEqualsSumOfWidths
) {
834 font_list
= FontList("Noto Sans UI,ui-sans, 12px");
835 SetFontRenderParamsDeviceScaleFactor(1.25f
);
836 #define WIDTH(x) GetStringWidthF(UTF8ToUTF16(x), font_list)
837 EXPECT_EQ(WIDTH("The administrator for this account has"),
838 WIDTH("The ") + WIDTH("administrator ") + WIDTH("for ") +
839 WIDTH("this ") + WIDTH("account ") + WIDTH("has"));
841 SetFontRenderParamsDeviceScaleFactor(1.0f
);
843 #endif // OS_CHROMEOS
845 // TODO(338784): Enable this on android.
846 #if defined(OS_ANDROID)
847 #define MAYBE_ElideRectangleString DISABLED_ElideRectangleString
849 #define MAYBE_ElideRectangleString ElideRectangleString
851 TEST(TextEliderTest
, MAYBE_ElideRectangleString
) {
859 { "", 0, 0, false, "" },
860 { "", 1, 1, false, "" },
861 { "Hi, my name is\nTom", 0, 0, true, "..." },
862 { "Hi, my name is\nTom", 1, 0, true, "\n..." },
863 { "Hi, my name is\nTom", 0, 1, true, "..." },
864 { "Hi, my name is\nTom", 1, 1, true, "H\n..." },
865 { "Hi, my name is\nTom", 2, 1, true, "H\ni\n..." },
866 { "Hi, my name is\nTom", 3, 1, true, "H\ni\n,\n..." },
867 { "Hi, my name is\nTom", 4, 1, true, "H\ni\n,\n \n..." },
868 { "Hi, my name is\nTom", 5, 1, true, "H\ni\n,\n \nm\n..." },
869 { "Hi, my name is\nTom", 0, 2, true, "..." },
870 { "Hi, my name is\nTom", 1, 2, true, "Hi\n..." },
871 { "Hi, my name is\nTom", 2, 2, true, "Hi\n, \n..." },
872 { "Hi, my name is\nTom", 3, 2, true, "Hi\n, \nmy\n..." },
873 { "Hi, my name is\nTom", 4, 2, true, "Hi\n, \nmy\n n\n..." },
874 { "Hi, my name is\nTom", 5, 2, true, "Hi\n, \nmy\n n\nam\n..." },
875 { "Hi, my name is\nTom", 0, 3, true, "..." },
876 { "Hi, my name is\nTom", 1, 3, true, "Hi,\n..." },
877 { "Hi, my name is\nTom", 2, 3, true, "Hi,\n my\n..." },
878 { "Hi, my name is\nTom", 3, 3, true, "Hi,\n my\n na\n..." },
879 { "Hi, my name is\nTom", 4, 3, true, "Hi,\n my\n na\nme \n..." },
880 { "Hi, my name is\nTom", 5, 3, true, "Hi,\n my\n na\nme \nis\n..." },
881 { "Hi, my name is\nTom", 1, 4, true, "Hi, \n..." },
882 { "Hi, my name is\nTom", 2, 4, true, "Hi, \nmy n\n..." },
883 { "Hi, my name is\nTom", 3, 4, true, "Hi, \nmy n\name \n..." },
884 { "Hi, my name is\nTom", 4, 4, true, "Hi, \nmy n\name \nis\n..." },
885 { "Hi, my name is\nTom", 5, 4, false, "Hi, \nmy n\name \nis\nTom" },
886 { "Hi, my name is\nTom", 1, 5, true, "Hi, \n..." },
887 { "Hi, my name is\nTom", 2, 5, true, "Hi, \nmy na\n..." },
888 { "Hi, my name is\nTom", 3, 5, true, "Hi, \nmy na\nme \n..." },
889 { "Hi, my name is\nTom", 4, 5, true, "Hi, \nmy na\nme \nis\n..." },
890 { "Hi, my name is\nTom", 5, 5, false, "Hi, \nmy na\nme \nis\nTom" },
891 { "Hi, my name is\nTom", 1, 6, true, "Hi, \n..." },
892 { "Hi, my name is\nTom", 2, 6, true, "Hi, \nmy \n..." },
893 { "Hi, my name is\nTom", 3, 6, true, "Hi, \nmy \nname \n..." },
894 { "Hi, my name is\nTom", 4, 6, true, "Hi, \nmy \nname \nis\n..." },
895 { "Hi, my name is\nTom", 5, 6, false, "Hi, \nmy \nname \nis\nTom" },
896 { "Hi, my name is\nTom", 1, 7, true, "Hi, \n..." },
897 { "Hi, my name is\nTom", 2, 7, true, "Hi, \nmy \n..." },
898 { "Hi, my name is\nTom", 3, 7, true, "Hi, \nmy \nname \n..." },
899 { "Hi, my name is\nTom", 4, 7, true, "Hi, \nmy \nname \nis\n..." },
900 { "Hi, my name is\nTom", 5, 7, false, "Hi, \nmy \nname \nis\nTom" },
901 { "Hi, my name is\nTom", 1, 8, true, "Hi, my \n..." },
902 { "Hi, my name is\nTom", 2, 8, true, "Hi, my \nname \n..." },
903 { "Hi, my name is\nTom", 3, 8, true, "Hi, my \nname \nis\n..." },
904 { "Hi, my name is\nTom", 4, 8, false, "Hi, my \nname \nis\nTom" },
905 { "Hi, my name is\nTom", 1, 9, true, "Hi, my \n..." },
906 { "Hi, my name is\nTom", 2, 9, true, "Hi, my \nname is\n..." },
907 { "Hi, my name is\nTom", 3, 9, false, "Hi, my \nname is\nTom" },
908 { "Hi, my name is\nTom", 1, 10, true, "Hi, my \n..." },
909 { "Hi, my name is\nTom", 2, 10, true, "Hi, my \nname is\n..." },
910 { "Hi, my name is\nTom", 3, 10, false, "Hi, my \nname is\nTom" },
911 { "Hi, my name is\nTom", 1, 11, true, "Hi, my \n..." },
912 { "Hi, my name is\nTom", 2, 11, true, "Hi, my \nname is\n..." },
913 { "Hi, my name is\nTom", 3, 11, false, "Hi, my \nname is\nTom" },
914 { "Hi, my name is\nTom", 1, 12, true, "Hi, my \n..." },
915 { "Hi, my name is\nTom", 2, 12, true, "Hi, my \nname is\n..." },
916 { "Hi, my name is\nTom", 3, 12, false, "Hi, my \nname is\nTom" },
917 { "Hi, my name is\nTom", 1, 13, true, "Hi, my name \n..." },
918 { "Hi, my name is\nTom", 2, 13, true, "Hi, my name \nis\n..." },
919 { "Hi, my name is\nTom", 3, 13, false, "Hi, my name \nis\nTom" },
920 { "Hi, my name is\nTom", 1, 20, true, "Hi, my name is\n..." },
921 { "Hi, my name is\nTom", 2, 20, false, "Hi, my name is\nTom" },
922 { "Hi, my name is Tom", 1, 40, false, "Hi, my name is Tom" },
924 base::string16 output
;
925 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
926 EXPECT_EQ(cases
[i
].result
,
927 ElideRectangleString(UTF8ToUTF16(cases
[i
].input
),
928 cases
[i
].max_rows
, cases
[i
].max_cols
,
930 EXPECT_EQ(cases
[i
].output
, UTF16ToUTF8(output
));
934 // TODO(338784): Enable this on android.
935 #if defined(OS_ANDROID)
936 #define MAYBE_ElideRectangleStringNotStrict \
937 DISABLED_ElideRectangleStringNotStrict
939 #define MAYBE_ElideRectangleStringNotStrict ElideRectangleStringNotStrict
941 TEST(TextEliderTest
, MAYBE_ElideRectangleStringNotStrict
) {
949 { "", 0, 0, false, "" },
950 { "", 1, 1, false, "" },
951 { "Hi, my name_is\nDick", 0, 0, true, "..." },
952 { "Hi, my name_is\nDick", 1, 0, true, "\n..." },
953 { "Hi, my name_is\nDick", 0, 1, true, "..." },
954 { "Hi, my name_is\nDick", 1, 1, true, "H\n..." },
955 { "Hi, my name_is\nDick", 2, 1, true, "H\ni\n..." },
956 { "Hi, my name_is\nDick", 3, 1, true, "H\ni\n,\n..." },
957 { "Hi, my name_is\nDick", 4, 1, true, "H\ni\n,\n \n..." },
958 { "Hi, my name_is\nDick", 5, 1, true, "H\ni\n,\n \nm\n..." },
959 { "Hi, my name_is\nDick", 0, 2, true, "..." },
960 { "Hi, my name_is\nDick", 1, 2, true, "Hi\n..." },
961 { "Hi, my name_is\nDick", 2, 2, true, "Hi\n, \n..." },
962 { "Hi, my name_is\nDick", 3, 2, true, "Hi\n, \nmy\n..." },
963 { "Hi, my name_is\nDick", 4, 2, true, "Hi\n, \nmy\n n\n..." },
964 { "Hi, my name_is\nDick", 5, 2, true, "Hi\n, \nmy\n n\nam\n..." },
965 { "Hi, my name_is\nDick", 0, 3, true, "..." },
966 { "Hi, my name_is\nDick", 1, 3, true, "Hi,\n..." },
967 { "Hi, my name_is\nDick", 2, 3, true, "Hi,\n my\n..." },
968 { "Hi, my name_is\nDick", 3, 3, true, "Hi,\n my\n na\n..." },
969 { "Hi, my name_is\nDick", 4, 3, true, "Hi,\n my\n na\nme_\n..." },
970 { "Hi, my name_is\nDick", 5, 3, true, "Hi,\n my\n na\nme_\nis\n..." },
971 { "Hi, my name_is\nDick", 1, 4, true, "Hi, ..." },
972 { "Hi, my name_is\nDick", 2, 4, true, "Hi, my n\n..." },
973 { "Hi, my name_is\nDick", 3, 4, true, "Hi, my n\name_\n..." },
974 { "Hi, my name_is\nDick", 4, 4, true, "Hi, my n\name_\nis\n..." },
975 { "Hi, my name_is\nDick", 5, 4, false, "Hi, my n\name_\nis\nDick" },
976 { "Hi, my name_is\nDick", 1, 5, true, "Hi, ..." },
977 { "Hi, my name_is\nDick", 2, 5, true, "Hi, my na\n..." },
978 { "Hi, my name_is\nDick", 3, 5, true, "Hi, my na\nme_is\n..." },
979 { "Hi, my name_is\nDick", 4, 5, true, "Hi, my na\nme_is\n\n..." },
980 { "Hi, my name_is\nDick", 5, 5, false, "Hi, my na\nme_is\n\nDick" },
981 { "Hi, my name_is\nDick", 1, 6, true, "Hi, ..." },
982 { "Hi, my name_is\nDick", 2, 6, true, "Hi, my nam\n..." },
983 { "Hi, my name_is\nDick", 3, 6, true, "Hi, my nam\ne_is\n..." },
984 { "Hi, my name_is\nDick", 4, 6, false, "Hi, my nam\ne_is\nDick" },
985 { "Hi, my name_is\nDick", 5, 6, false, "Hi, my nam\ne_is\nDick" },
986 { "Hi, my name_is\nDick", 1, 7, true, "Hi, ..." },
987 { "Hi, my name_is\nDick", 2, 7, true, "Hi, my name\n..." },
988 { "Hi, my name_is\nDick", 3, 7, true, "Hi, my name\n_is\n..." },
989 { "Hi, my name_is\nDick", 4, 7, false, "Hi, my name\n_is\nDick" },
990 { "Hi, my name_is\nDick", 5, 7, false, "Hi, my name\n_is\nDick" },
991 { "Hi, my name_is\nDick", 1, 8, true, "Hi, my n\n..." },
992 { "Hi, my name_is\nDick", 2, 8, true, "Hi, my n\name_is\n..." },
993 { "Hi, my name_is\nDick", 3, 8, false, "Hi, my n\name_is\nDick" },
994 { "Hi, my name_is\nDick", 1, 9, true, "Hi, my ..." },
995 { "Hi, my name_is\nDick", 2, 9, true, "Hi, my name_is\n..." },
996 { "Hi, my name_is\nDick", 3, 9, false, "Hi, my name_is\nDick" },
997 { "Hi, my name_is\nDick", 1, 10, true, "Hi, my ..." },
998 { "Hi, my name_is\nDick", 2, 10, true, "Hi, my name_is\n..." },
999 { "Hi, my name_is\nDick", 3, 10, false, "Hi, my name_is\nDick" },
1000 { "Hi, my name_is\nDick", 1, 11, true, "Hi, my ..." },
1001 { "Hi, my name_is\nDick", 2, 11, true, "Hi, my name_is\n..." },
1002 { "Hi, my name_is\nDick", 3, 11, false, "Hi, my name_is\nDick" },
1003 { "Hi, my name_is\nDick", 1, 12, true, "Hi, my ..." },
1004 { "Hi, my name_is\nDick", 2, 12, true, "Hi, my name_is\n..." },
1005 { "Hi, my name_is\nDick", 3, 12, false, "Hi, my name_is\nDick" },
1006 { "Hi, my name_is\nDick", 1, 13, true, "Hi, my ..." },
1007 { "Hi, my name_is\nDick", 2, 13, true, "Hi, my name_is\n..." },
1008 { "Hi, my name_is\nDick", 3, 13, false, "Hi, my name_is\nDick" },
1009 { "Hi, my name_is\nDick", 1, 20, true, "Hi, my name_is\n..." },
1010 { "Hi, my name_is\nDick", 2, 20, false, "Hi, my name_is\nDick" },
1011 { "Hi, my name_is Dick", 1, 40, false, "Hi, my name_is Dick" },
1013 base::string16 output
;
1014 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
1015 EXPECT_EQ(cases
[i
].result
,
1016 ElideRectangleString(UTF8ToUTF16(cases
[i
].input
),
1017 cases
[i
].max_rows
, cases
[i
].max_cols
,
1019 EXPECT_EQ(cases
[i
].output
, UTF16ToUTF8(output
));
1023 // TODO(338784): Enable this on android.
1024 #if defined(OS_ANDROID)
1025 #define MAYBE_ElideRectangleWide16 DISABLED_ElideRectangleWide16
1027 #define MAYBE_ElideRectangleWide16 ElideRectangleWide16
1029 TEST(TextEliderTest
, MAYBE_ElideRectangleWide16
) {
1030 // Two greek words separated by space.
1031 const base::string16
str(WideToUTF16(
1032 L
"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
1033 L
"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2"));
1034 const base::string16
out1(WideToUTF16(
1035 L
"\x03a0\x03b1\x03b3\x03ba\n"
1036 L
"\x03cc\x03c3\x03bc\x03b9\n"
1038 const base::string16
out2(WideToUTF16(
1039 L
"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9\x03bf\x03c2\x0020\n"
1040 L
"\x0399\x03c3\x03c4\x03cc\x03c2"));
1041 base::string16 output
;
1042 EXPECT_TRUE(ElideRectangleString(str
, 2, 4, true, &output
));
1043 EXPECT_EQ(out1
, output
);
1044 EXPECT_FALSE(ElideRectangleString(str
, 2, 12, true, &output
));
1045 EXPECT_EQ(out2
, output
);
1048 // TODO(338784): Enable this on android.
1049 #if defined(OS_ANDROID)
1050 #define MAYBE_ElideRectangleWide32 DISABLED_ElideRectangleWide32
1052 #define MAYBE_ElideRectangleWide32 ElideRectangleWide32
1054 TEST(TextEliderTest
, MAYBE_ElideRectangleWide32
) {
1055 // Four U+1D49C MATHEMATICAL SCRIPT CAPITAL A followed by space "aaaaa".
1056 const base::string16
str(UTF8ToUTF16(
1057 "\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C"
1059 const base::string16
out(UTF8ToUTF16(
1060 "\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\n"
1061 "\xF0\x9D\x92\x9C \naaa\n..."));
1062 base::string16 output
;
1063 EXPECT_TRUE(ElideRectangleString(str
, 3, 3, true, &output
));
1064 EXPECT_EQ(out
, output
);
1067 // TODO(338784): Enable this on android.
1068 #if defined(OS_ANDROID)
1069 #define MAYBE_TruncateString DISABLED_TruncateString
1071 #define MAYBE_TruncateString TruncateString
1073 TEST(TextEliderTest
, MAYBE_TruncateString
) {
1074 base::string16 string
= ASCIIToUTF16("foooooey bxxxar baz");
1076 // Tests that apply to both break behaviors:
1078 // Make sure it doesn't modify the string if length > string length.
1079 EXPECT_EQ(string
, TruncateString(string
, 100, WORD_BREAK
));
1080 EXPECT_EQ(string
, TruncateString(string
, 100, CHARACTER_BREAK
));
1082 // Test no characters.
1083 EXPECT_EQ(L
"", UTF16ToWide(TruncateString(string
, 0, WORD_BREAK
)));
1084 EXPECT_EQ(L
"", UTF16ToWide(TruncateString(string
, 0, CHARACTER_BREAK
)));
1086 // Test 1 character.
1087 EXPECT_EQ(L
"\x2026", UTF16ToWide(TruncateString(string
, 1, WORD_BREAK
)));
1088 EXPECT_EQ(L
"\x2026", UTF16ToWide(TruncateString(string
, 1, CHARACTER_BREAK
)));
1090 // Test completely truncates string if break is on initial whitespace.
1091 EXPECT_EQ(L
"\x2026",
1092 UTF16ToWide(TruncateString(ASCIIToUTF16(" "), 2, WORD_BREAK
)));
1093 EXPECT_EQ(L
"\x2026",
1094 UTF16ToWide(TruncateString(ASCIIToUTF16(" "), 2,
1097 // Break-only-at-word-boundaries tests:
1099 // Test adds ... at right spot when there is enough room to break at a
1101 EXPECT_EQ(L
"foooooey\x2026", UTF16ToWide(TruncateString(string
, 14,
1104 // Test adds ... at right spot when there is not enough space in first word.
1105 EXPECT_EQ(L
"f\x2026", UTF16ToWide(TruncateString(string
, 2, WORD_BREAK
)));
1107 // Test adds ... at right spot when there is not enough room to break at a
1109 EXPECT_EQ(L
"foooooey\x2026", UTF16ToWide(TruncateString(string
, 11,
1112 // Break-anywhere tests:
1114 // Test adds ... at right spot within a word.
1115 EXPECT_EQ(L
"f\x2026", UTF16ToWide(TruncateString(string
, 2,
1118 // Test removes trailing whitespace if break falls between words.
1119 EXPECT_EQ(L
"foooooey\x2026", UTF16ToWide(TruncateString(string
, 12,