Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / ui / gfx / text_elider_unittest.cc
blobb0baa4226da611604a608d00ee9a3d7785e876f2
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.
4 //
5 // Unit tests for eliding and formatting utility functions.
7 #include "ui/gfx/text_elider.h"
9 #include <vector>
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;
28 namespace gfx {
30 namespace {
32 struct Testcase {
33 const std::string input;
34 const std::string output;
37 struct FileTestcase {
38 const base::FilePath::StringType input;
39 const std::string output;
42 struct UTF16Testcase {
43 const base::string16 input;
44 const base::string16 output;
47 struct TestData {
48 const std::string a;
49 const std::string b;
50 const int compare_result;
53 } // namespace
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
60 #else
61 #define MAYBE_ElideEmail ElideEmail
62 #endif
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[] = {
73 {"g@g.c", "g@g.c"},
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),
114 ELIDE_EMAIL));
118 // TODO(338784): Enable this on android.
119 #if defined(OS_ANDROID)
120 #define MAYBE_ElideEmailMoreSpace DISABLED_ElideEmailMoreSpace
121 #else
122 #define MAYBE_ElideEmailMoreSpace ElideEmailMoreSpace
123 #endif
124 TEST(TextEliderTest, MAYBE_ElideEmailMoreSpace) {
125 const int test_width_factors[] = {
126 100,
127 10000,
128 1000000,
130 const std::string test_emails[] = {
131 "a@c",
132 "test@email.com",
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
155 #else
156 #define MAYBE_TestFilenameEliding TestFilenameEliding
157 #endif
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"),
171 "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
210 #else
211 #define MAYBE_ElideTextTruncate ElideTextTruncate
212 #endif
213 TEST(TextEliderTest, MAYBE_ElideTextTruncate) {
214 const FontList font_list;
215 const float kTestWidth = GetStringWidthF(ASCIIToUTF16("Test"), font_list);
216 struct TestData {
217 const char* input;
218 float width;
219 const char* output;
220 } cases[] = {
221 { "", 0, "" },
222 { "Test", 0, "" },
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
239 #else
240 #define MAYBE_ElideTextEllipsis ElideTextEllipsis
241 #endif
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);
248 struct TestData {
249 const char* input;
250 float width;
251 const char* output;
252 } cases[] = {
253 { "", 0, "" },
254 { "Test", 0, "" },
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
271 #else
272 #define MAYBE_ElideTextEllipsisFront ElideTextEllipsisFront
273 #endif
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);
282 struct TestData {
283 const char* input;
284 float width;
285 const base::string16 output;
286 } cases[] = {
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
323 #else
324 #define MAYBE_ElideTextAtomicSequences ElideTextAtomicSequences
325 #endif
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
367 #else
368 #define MAYBE_ElideTextLongStrings ElideTextLongStrings
369 #endif
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),
405 ELIDE_TAIL).size());
406 EXPECT_EQ(kEllipsisStr,
407 ElideText(testcases_end[i].input, font_list, ellipsis_width,
408 ELIDE_TAIL));
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,
433 ELIDE_MIDDLE));
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(),
448 ElideText(
449 testcases_beginning[i].input, font_list,
450 GetStringWidthF(testcases_beginning[i].output, font_list),
451 ELIDE_HEAD).size());
452 EXPECT_EQ(kEllipsisStr,
453 ElideText(testcases_beginning[i].input, font_list, ellipsis_width,
454 ELIDE_HEAD));
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,
514 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.
535 // First 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));
540 // Second sequence:
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));
544 // Third sequence:
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) {
589 struct TestData {
590 const char* input;
591 int max_len;
592 bool result;
593 const char* output;
594 } cases[] = {
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
619 #else
620 #define MAYBE_ElideRectangleText ElideRectangleText
621 #endif
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);
627 struct TestData {
628 const char* input;
629 float available_pixel_width;
630 int available_pixel_height;
631 bool truncated_y;
632 const char* output;
633 } cases[] = {
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),
665 font_list,
666 cases[i].available_pixel_width,
667 cases[i].available_pixel_height,
668 TRUNCATE_LONG_WORDS,
669 &lines));
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!";
674 } else {
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
684 #else
685 #define MAYBE_ElideRectangleTextPunctuation ElideRectangleTextPunctuation
686 #endif
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);
693 struct TestData {
694 const char* input;
695 float available_pixel_width;
696 int available_pixel_height;
697 bool wrap_words;
698 bool truncated_x;
699 const char* output;
700 } cases[] = {
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),
713 font_list,
714 cases[i].available_pixel_width,
715 cases[i].available_pixel_height,
716 wrap_behavior,
717 &lines));
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!";
722 } else {
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
731 #else
732 #define MAYBE_ElideRectangleTextLongWords ElideRectangleTextLongWords
733 #endif
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);
742 struct TestData {
743 const char* input;
744 float available_pixel_width;
745 WordWrapBehavior wrap_behavior;
746 bool truncated_x;
747 const char* output;
748 } cases[] = {
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),
782 font_list,
783 cases[i].available_pixel_width,
784 kAvailableHeight,
785 cases[i].wrap_behavior,
786 &lines));
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
804 #else
805 #define MAYBE_ElideRectangleTextCheckLineWidth ElideRectangleTextCheckLineWidth
806 #endif
807 TEST(TextEliderTest, MAYBE_ElideRectangleTextCheckLineWidth) {
808 FontList font_list;
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));
812 #endif
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),
818 font_list,
819 kAvailableWidth,
820 kAvailableHeight,
821 WRAP_LONG_WORDS,
822 &lines));
823 ASSERT_EQ(2u, lines.size());
824 EXPECT_LE(GetStringWidthF(lines[0], font_list), kAvailableWidth);
825 EXPECT_LE(GetStringWidthF(lines[1], font_list), kAvailableWidth);
828 #ifdef OS_CHROMEOS
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
831 // words.
832 TEST(TextEliderTest, ElideRectangleTextCheckConcatWidthEqualsSumOfWidths) {
833 FontList font_list;
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"));
840 #undef WIDTH
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
848 #else
849 #define MAYBE_ElideRectangleString ElideRectangleString
850 #endif
851 TEST(TextEliderTest, MAYBE_ElideRectangleString) {
852 struct TestData {
853 const char* input;
854 int max_rows;
855 int max_cols;
856 bool result;
857 const char* output;
858 } cases[] = {
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,
929 true, &output));
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
938 #else
939 #define MAYBE_ElideRectangleStringNotStrict ElideRectangleStringNotStrict
940 #endif
941 TEST(TextEliderTest, MAYBE_ElideRectangleStringNotStrict) {
942 struct TestData {
943 const char* input;
944 int max_rows;
945 int max_cols;
946 bool result;
947 const char* output;
948 } cases[] = {
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,
1018 false, &output));
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
1026 #else
1027 #define MAYBE_ElideRectangleWide16 ElideRectangleWide16
1028 #endif
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"
1037 L"..."));
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
1051 #else
1052 #define MAYBE_ElideRectangleWide32 ElideRectangleWide32
1053 #endif
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"
1058 " aaaaa"));
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
1070 #else
1071 #define MAYBE_TruncateString TruncateString
1072 #endif
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,
1095 CHARACTER_BREAK)));
1097 // Break-only-at-word-boundaries tests:
1099 // Test adds ... at right spot when there is enough room to break at a
1100 // word boundary.
1101 EXPECT_EQ(L"foooooey\x2026", UTF16ToWide(TruncateString(string, 14,
1102 WORD_BREAK)));
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
1108 // word boundary.
1109 EXPECT_EQ(L"foooooey\x2026", UTF16ToWide(TruncateString(string, 11,
1110 WORD_BREAK)));
1112 // Break-anywhere tests:
1114 // Test adds ... at right spot within a word.
1115 EXPECT_EQ(L"f\x2026", UTF16ToWide(TruncateString(string, 2,
1116 CHARACTER_BREAK)));
1118 // Test removes trailing whitespace if break falls between words.
1119 EXPECT_EQ(L"foooooey\x2026", UTF16ToWide(TruncateString(string, 12,
1120 CHARACTER_BREAK)));
1123 } // namespace gfx