Home | History | Annotate | Download | only in text
      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.
      6 
      7 #include "ui/base/text/text_elider.h"
      8 
      9 #include "base/files/file_path.h"
     10 #include "base/i18n/rtl.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 #include "ui/gfx/font.h"
     16 #include "url/gurl.h"
     17 
     18 namespace ui {
     19 
     20 namespace {
     21 
     22 struct Testcase {
     23   const std::string input;
     24   const std::string output;
     25 };
     26 
     27 struct FileTestcase {
     28   const base::FilePath::StringType input;
     29   const std::string output;
     30 };
     31 
     32 struct UTF16Testcase {
     33   const string16 input;
     34   const string16 output;
     35 };
     36 
     37 struct TestData {
     38   const std::string a;
     39   const std::string b;
     40   const int compare_result;
     41 };
     42 
     43 void RunUrlTest(Testcase* testcases, size_t num_testcases) {
     44   static const gfx::Font font;
     45   for (size_t i = 0; i < num_testcases; ++i) {
     46     const GURL url(testcases[i].input);
     47     // Should we test with non-empty language list?
     48     // That's kinda redundant with net_util_unittests.
     49     EXPECT_EQ(UTF8ToUTF16(testcases[i].output),
     50               ElideUrl(url, font,
     51                        font.GetStringWidth(UTF8ToUTF16(testcases[i].output)),
     52                        std::string()));
     53   }
     54 }
     55 
     56 }  // namespace
     57 
     58 // TODO(ios): Complex eliding is off by one for some of those tests on iOS.
     59 // See crbug.com/154019
     60 #if defined(OS_IOS)
     61 #define MAYBE_ElideEmail DISABLED_ElideEmail
     62 #else
     63 #define MAYBE_ElideEmail ElideEmail
     64 #endif
     65 TEST(TextEliderTest, MAYBE_ElideEmail) {
     66   const std::string kEllipsisStr(kEllipsis);
     67 
     68   // Test emails and their expected elided forms (from which the available
     69   // widths will be derived).
     70   // For elided forms in which both the username and domain must be elided:
     71   // the result (how many characters are left on each side) can be font
     72   // dependent. To avoid this, the username is prefixed with the characters
     73   // expected to remain in the domain.
     74   Testcase testcases[] = {
     75       {"g (at) g.c", "g (at) g.c"},
     76       {"g (at) g.c", kEllipsisStr},
     77       {"ga (at) co.ca", "ga@c" + kEllipsisStr + "a"},
     78       {"short (at) small.com", "s" + kEllipsisStr + "@s" + kEllipsisStr},
     79       {"short (at) small.com", "s" + kEllipsisStr + "@small.com"},
     80       {"short (at) longbutlotsofspace.com", "short (at) longbutlotsofspace.com"},
     81       {"short (at) longbutnotverymuchspace.com",
     82        "short@long" + kEllipsisStr + ".com"},
     83       {"la_short (at) longbutverytightspace.ca",
     84        "la" + kEllipsisStr + "@l" + kEllipsisStr + "a"},
     85       {"longusername (at) gmail.com", "long" + kEllipsisStr + "@gmail.com"},
     86       {"elidetothemax (at) justfits.com", "e" + kEllipsisStr + "@justfits.com"},
     87       {"thatom_somelongemail (at) thatdoesntfit.com",
     88        "thatom" + kEllipsisStr + "@tha" + kEllipsisStr + "om"},
     89       {"namefits (at) butthedomaindoesnt.com",
     90        "namefits@butthedo" + kEllipsisStr + "snt.com"},
     91       {"widthtootight (at) nospace.com", kEllipsisStr},
     92       {"nospaceforusername@l", kEllipsisStr},
     93       {"little (at) littlespace.com", "l" + kEllipsisStr + "@l" + kEllipsisStr},
     94       {"l (at) llllllllllllllllllllllll.com", "l@lllll" + kEllipsisStr + ".com"},
     95       {"messed\"up@whyanat\"++@notgoogley.com",
     96        "messed\"up@whyanat\"++@notgoogley.com"},
     97       {"messed\"up@whyanat\"++@notgoogley.com",
     98        "messed\"up@why" + kEllipsisStr + "@notgoogley.com"},
     99       {"noca_messed\"up@whyanat\"++@notgoogley.ca",
    100        "noca" + kEllipsisStr + "@no" + kEllipsisStr + "ca"},
    101       {"at\"@@@@@@@@@...@@.@.@.@@@\"@madness.com",
    102        "at\"@@@@@@@@@...@@.@." + kEllipsisStr + "@madness.com"},
    103       // Special case: "m..." takes more than half of the available width; thus
    104       // the domain must elide to "l..." and not "l...l" as it must allow enough
    105       // space for the minimal username elision although its half of the
    106       // available width would normally allow it to elide to "l...l".
    107       {"mmmmm@llllllllll", "m" + kEllipsisStr + "@l" + kEllipsisStr},
    108   };
    109 
    110   const gfx::Font font;
    111   for (size_t i = 0; i < arraysize(testcases); ++i) {
    112     const string16 expected_output = UTF8ToUTF16(testcases[i].output);
    113     EXPECT_EQ(expected_output,
    114               ElideEmail(UTF8ToUTF16(testcases[i].input),
    115                          font,
    116                          font.GetStringWidth(expected_output)));
    117   }
    118 }
    119 
    120 TEST(TextEliderTest, ElideEmailMoreSpace) {
    121   const int test_width_factors[] = {
    122       100,
    123       10000,
    124       1000000,
    125   };
    126   const std::string test_emails[] = {
    127       "a@c",
    128       "test (at) email.com",
    129       "short (at) verysuperdupperlongdomain.com",
    130       "supermegalongusername (at) withasuperlonnnggggdomain.gouv.qc.ca",
    131   };
    132 
    133   const gfx::Font font;
    134   for (size_t i = 0; i < arraysize(test_width_factors); ++i) {
    135     const int test_width = test_width_factors[i] *
    136                            font.GetAverageCharacterWidth();
    137     for (size_t j = 0; j < arraysize(test_emails); ++j) {
    138       // Extra space is available: the email should not be elided.
    139       const string16 test_email = UTF8ToUTF16(test_emails[j]);
    140       EXPECT_EQ(test_email, ElideEmail(test_email, font, test_width));
    141     }
    142   }
    143 }
    144 
    145 // Test eliding of commonplace URLs.
    146 TEST(TextEliderTest, TestGeneralEliding) {
    147   const std::string kEllipsisStr(kEllipsis);
    148   Testcase testcases[] = {
    149     {"http://www.google.com/intl/en/ads/",
    150      "www.google.com/intl/en/ads/"},
    151     {"http://www.google.com/intl/en/ads/", "www.google.com/intl/en/ads/"},
    152     {"http://www.google.com/intl/en/ads/",
    153      "google.com/intl/" + kEllipsisStr + "/ads/"},
    154     {"http://www.google.com/intl/en/ads/",
    155      "google.com/" + kEllipsisStr + "/ads/"},
    156     {"http://www.google.com/intl/en/ads/", "google.com/" + kEllipsisStr},
    157     {"http://www.google.com/intl/en/ads/", "goog" + kEllipsisStr},
    158     {"https://subdomain.foo.com/bar/filename.html",
    159      "subdomain.foo.com/bar/filename.html"},
    160     {"https://subdomain.foo.com/bar/filename.html",
    161      "subdomain.foo.com/" + kEllipsisStr + "/filename.html"},
    162     {"http://subdomain.foo.com/bar/filename.html",
    163      kEllipsisStr + "foo.com/" + kEllipsisStr + "/filename.html"},
    164     {"http://www.google.com/intl/en/ads/?aLongQueryWhichIsNotRequired",
    165      "www.google.com/intl/en/ads/?aLongQ" + kEllipsisStr},
    166   };
    167 
    168   RunUrlTest(testcases, arraysize(testcases));
    169 }
    170 
    171 // When there is very little space available, the elision code will shorten
    172 // both path AND file name to an ellipsis - ".../...". To avoid this result,
    173 // there is a hack in place that simply treats them as one string in this
    174 // case.
    175 TEST(TextEliderTest, TestTrailingEllipsisSlashEllipsisHack) {
    176   const std::string kEllipsisStr(kEllipsis);
    177 
    178   // Very little space, would cause double ellipsis.
    179   gfx::Font font;
    180   GURL url("http://battersbox.com/directory/foo/peter_paul_and_mary.html");
    181   int available_width = font.GetStringWidth(
    182       UTF8ToUTF16("battersbox.com/" + kEllipsisStr + "/" + kEllipsisStr));
    183 
    184   // Create the expected string, after elision. Depending on font size, the
    185   // directory might become /dir... or /di... or/d... - it never should be
    186   // shorter than that. (If it is, the font considers d... to be longer
    187   // than .../... -  that should never happen).
    188   ASSERT_GT(font.GetStringWidth(UTF8ToUTF16(kEllipsisStr + "/" + kEllipsisStr)),
    189       font.GetStringWidth(UTF8ToUTF16("d" + kEllipsisStr)));
    190   GURL long_url("http://battersbox.com/directorynameisreallylongtoforcetrunc");
    191   string16 expected = ElideUrl(long_url, font, available_width, std::string());
    192   // Ensure that the expected result still contains part of the directory name.
    193   ASSERT_GT(expected.length(), std::string("battersbox.com/d").length());
    194   EXPECT_EQ(expected,
    195              ElideUrl(url, font, available_width, std::string()));
    196 
    197   // More space available - elide directories, partially elide filename.
    198   Testcase testcases[] = {
    199     {"http://battersbox.com/directory/foo/peter_paul_and_mary.html",
    200      "battersbox.com/" + kEllipsisStr + "/peter" + kEllipsisStr},
    201   };
    202   RunUrlTest(testcases, arraysize(testcases));
    203 }
    204 
    205 // Test eliding of empty strings, URLs with ports, passwords, queries, etc.
    206 TEST(TextEliderTest, TestMoreEliding) {
    207   const std::string kEllipsisStr(kEllipsis);
    208   Testcase testcases[] = {
    209     {"http://www.google.com/foo?bar", "www.google.com/foo?bar"},
    210     {"http://xyz.google.com/foo?bar", "xyz.google.com/foo?" + kEllipsisStr},
    211     {"http://xyz.google.com/foo?bar", "xyz.google.com/foo" + kEllipsisStr},
    212     {"http://xyz.google.com/foo?bar", "xyz.google.com/fo" + kEllipsisStr},
    213     {"http://a.b.com/pathname/c?d", "a.b.com/" + kEllipsisStr + "/c?d"},
    214     {"", ""},
    215     {"http://foo.bar..example.com...hello/test/filename.html",
    216      "foo.bar..example.com...hello/" + kEllipsisStr + "/filename.html"},
    217     {"http://foo.bar../", "foo.bar.."},
    218     {"http://xn--1lq90i.cn/foo", "\xe5\x8c\x97\xe4\xba\xac.cn/foo"},
    219     {"http://me:mypass@secrethost.com:99/foo?bar#baz",
    220      "secrethost.com:99/foo?bar#baz"},
    221     {"http://me:mypass@ss%xxfdsf.com/foo", "ss%25xxfdsf.com/foo"},
    222     {"mailto:elgoato (at) elgoato.com", "mailto:elgoato (at) elgoato.com"},
    223     {"javascript:click(0)", "javascript:click(0)"},
    224     {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename",
    225      "chess.eecs.berkeley.edu:4430/login/arbitfilename"},
    226     {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename",
    227      kEllipsisStr + "berkeley.edu:4430/" + kEllipsisStr + "/arbitfilename"},
    228 
    229     // Unescaping.
    230     {"http://www/%E4%BD%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0",
    231      "www/\xe4\xbd\xa0\xe5\xa5\xbd?q=\xe4\xbd\xa0\xe5\xa5\xbd#\xe4\xbd\xa0"},
    232 
    233     // Invalid unescaping for path. The ref will always be valid UTF-8. We don't
    234     // bother to do too many edge cases, since these are handled by the escaper
    235     // unittest.
    236     {"http://www/%E4%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0",
    237      "www/%E4%A0%E5%A5%BD?q=\xe4\xbd\xa0\xe5\xa5\xbd#\xe4\xbd\xa0"},
    238   };
    239 
    240   RunUrlTest(testcases, arraysize(testcases));
    241 }
    242 
    243 // Test eliding of file: URLs.
    244 TEST(TextEliderTest, TestFileURLEliding) {
    245   const std::string kEllipsisStr(kEllipsis);
    246   Testcase testcases[] = {
    247     {"file:///C:/path1/path2/path3/filename",
    248      "file:///C:/path1/path2/path3/filename"},
    249     {"file:///C:/path1/path2/path3/filename",
    250      "C:/path1/path2/path3/filename"},
    251 // GURL parses "file:///C:path" differently on windows than it does on posix.
    252 #if defined(OS_WIN)
    253     {"file:///C:path1/path2/path3/filename",
    254      "C:/path1/path2/" + kEllipsisStr + "/filename"},
    255     {"file:///C:path1/path2/path3/filename",
    256      "C:/path1/" + kEllipsisStr + "/filename"},
    257     {"file:///C:path1/path2/path3/filename",
    258      "C:/" + kEllipsisStr + "/filename"},
    259 #endif
    260     {"file://filer/foo/bar/file", "filer/foo/bar/file"},
    261     {"file://filer/foo/bar/file", "filer/foo/" + kEllipsisStr + "/file"},
    262     {"file://filer/foo/bar/file", "filer/" + kEllipsisStr + "/file"},
    263   };
    264 
    265   RunUrlTest(testcases, arraysize(testcases));
    266 }
    267 
    268 // TODO(ios): Complex eliding is off by one for some of those tests on iOS.
    269 // See crbug.com/154019
    270 #if defined(OS_IOS)
    271 #define MAYBE_TestFilenameEliding DISABLED_TestFilenameEliding
    272 #else
    273 #define MAYBE_TestFilenameEliding TestFilenameEliding
    274 #endif
    275 TEST(TextEliderTest, MAYBE_TestFilenameEliding) {
    276   const std::string kEllipsisStr(kEllipsis);
    277   const base::FilePath::StringType kPathSeparator =
    278       base::FilePath::StringType().append(1, base::FilePath::kSeparators[0]);
    279 
    280   FileTestcase testcases[] = {
    281     {FILE_PATH_LITERAL(""), ""},
    282     {FILE_PATH_LITERAL("."), "."},
    283     {FILE_PATH_LITERAL("filename.exe"), "filename.exe"},
    284     {FILE_PATH_LITERAL(".longext"), ".longext"},
    285     {FILE_PATH_LITERAL("pie"), "pie"},
    286     {FILE_PATH_LITERAL("c:") + kPathSeparator + FILE_PATH_LITERAL("path") +
    287       kPathSeparator + FILE_PATH_LITERAL("filename.pie"),
    288       "filename.pie"},
    289     {FILE_PATH_LITERAL("c:") + kPathSeparator + FILE_PATH_LITERAL("path") +
    290       kPathSeparator + FILE_PATH_LITERAL("longfilename.pie"),
    291       "long" + kEllipsisStr + ".pie"},
    292     {FILE_PATH_LITERAL("http://path.com/filename.pie"), "filename.pie"},
    293     {FILE_PATH_LITERAL("http://path.com/longfilename.pie"),
    294       "long" + kEllipsisStr + ".pie"},
    295     {FILE_PATH_LITERAL("piesmashingtacularpants"), "pie" + kEllipsisStr},
    296     {FILE_PATH_LITERAL(".piesmashingtacularpants"), ".pie" + kEllipsisStr},
    297     {FILE_PATH_LITERAL("cheese."), "cheese."},
    298     {FILE_PATH_LITERAL("file name.longext"),
    299       "file" + kEllipsisStr + ".longext"},
    300     {FILE_PATH_LITERAL("fil ename.longext"),
    301       "fil " + kEllipsisStr + ".longext"},
    302     {FILE_PATH_LITERAL("filename.longext"),
    303       "file" + kEllipsisStr + ".longext"},
    304     {FILE_PATH_LITERAL("filename.middleext.longext"),
    305       "filename.mid" + kEllipsisStr + ".longext"},
    306     {FILE_PATH_LITERAL("filename.superduperextremelylongext"),
    307       "filename.sup" + kEllipsisStr + "emelylongext"},
    308     {FILE_PATH_LITERAL("filenamereallylongtext.superduperextremelylongext"),
    309       "filenamereall" + kEllipsisStr + "emelylongext"},
    310     {FILE_PATH_LITERAL("file.name.really.long.text.superduperextremelylongext"),
    311       "file.name.re" + kEllipsisStr + "emelylongext"}
    312   };
    313 
    314   static const gfx::Font font;
    315   for (size_t i = 0; i < arraysize(testcases); ++i) {
    316     base::FilePath filepath(testcases[i].input);
    317     string16 expected = UTF8ToUTF16(testcases[i].output);
    318     expected = base::i18n::GetDisplayStringInLTRDirectionality(expected);
    319     EXPECT_EQ(expected, ElideFilename(filepath,
    320         font,
    321         font.GetStringWidth(UTF8ToUTF16(testcases[i].output))));
    322   }
    323 }
    324 
    325 TEST(TextEliderTest, ElideTextTruncate) {
    326   const gfx::Font font;
    327   const int kTestWidth = font.GetStringWidth(ASCIIToUTF16("Test"));
    328   struct TestData {
    329     const char* input;
    330     int width;
    331     const char* output;
    332   } cases[] = {
    333     { "", 0, "" },
    334     { "Test", 0, "" },
    335     { "", kTestWidth, "" },
    336     { "Tes", kTestWidth, "Tes" },
    337     { "Test", kTestWidth, "Test" },
    338     { "Tests", kTestWidth, "Test" },
    339   };
    340 
    341   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    342     string16 result = ElideText(UTF8ToUTF16(cases[i].input), font,
    343                                 cases[i].width, TRUNCATE_AT_END);
    344     EXPECT_EQ(cases[i].output, UTF16ToUTF8(result));
    345   }
    346 }
    347 
    348 TEST(TextEliderTest, ElideTextEllipsis) {
    349   const gfx::Font font;
    350   const int kTestWidth = font.GetStringWidth(ASCIIToUTF16("Test"));
    351   const char* kEllipsis = "\xE2\x80\xA6";
    352   const int kEllipsisWidth = font.GetStringWidth(UTF8ToUTF16(kEllipsis));
    353   struct TestData {
    354     const char* input;
    355     int width;
    356     const char* output;
    357   } cases[] = {
    358     { "", 0, "" },
    359     { "Test", 0, "" },
    360     { "Test", kEllipsisWidth, kEllipsis },
    361     { "", kTestWidth, "" },
    362     { "Tes", kTestWidth, "Tes" },
    363     { "Test", kTestWidth, "Test" },
    364   };
    365 
    366   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    367     string16 result = ElideText(UTF8ToUTF16(cases[i].input), font,
    368                                 cases[i].width, ELIDE_AT_END);
    369     EXPECT_EQ(cases[i].output, UTF16ToUTF8(result));
    370   }
    371 }
    372 
    373 // Checks that all occurrences of |first_char| are followed by |second_char| and
    374 // all occurrences of |second_char| are preceded by |first_char| in |text|.
    375 static void CheckSurrogatePairs(const string16& text,
    376                                 char16 first_char,
    377                                 char16 second_char) {
    378   size_t index = text.find_first_of(first_char);
    379   while (index != string16::npos) {
    380     EXPECT_LT(index, text.length() - 1);
    381     EXPECT_EQ(second_char, text[index + 1]);
    382     index = text.find_first_of(first_char, index + 1);
    383   }
    384   index = text.find_first_of(second_char);
    385   while (index != string16::npos) {
    386     EXPECT_GT(index, 0U);
    387     EXPECT_EQ(first_char, text[index - 1]);
    388     index = text.find_first_of(second_char, index + 1);
    389   }
    390 }
    391 
    392 TEST(TextEliderTest, ElideTextSurrogatePairs) {
    393   const gfx::Font font;
    394   // The below is 'MUSICAL SYMBOL G CLEF', which is represented in UTF-16 as
    395   // two characters forming a surrogate pair 0x0001D11E.
    396   const std::string kSurrogate = "\xF0\x9D\x84\x9E";
    397   const string16 kTestString =
    398       UTF8ToUTF16(kSurrogate + "ab" + kSurrogate + kSurrogate + "cd");
    399   const int kTestStringWidth = font.GetStringWidth(kTestString);
    400   const char16 kSurrogateFirstChar = kTestString[0];
    401   const char16 kSurrogateSecondChar = kTestString[1];
    402   string16 result;
    403 
    404   // Elide |kTextString| to all possible widths and check that no instance of
    405   // |kSurrogate| was split in two.
    406   for (int width = 0; width <= kTestStringWidth; width++) {
    407     result = ElideText(kTestString, font, width, TRUNCATE_AT_END);
    408     CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar);
    409 
    410     result = ElideText(kTestString, font, width, ELIDE_AT_END);
    411     CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar);
    412 
    413     result = ElideText(kTestString, font, width, ELIDE_IN_MIDDLE);
    414     CheckSurrogatePairs(result, kSurrogateFirstChar, kSurrogateSecondChar);
    415   }
    416 }
    417 
    418 TEST(TextEliderTest, ElideTextLongStrings) {
    419   const string16 kEllipsisStr = UTF8ToUTF16(kEllipsis);
    420   string16 data_scheme(UTF8ToUTF16("data:text/plain,"));
    421   size_t data_scheme_length = data_scheme.length();
    422 
    423   string16 ten_a(10, 'a');
    424   string16 hundred_a(100, 'a');
    425   string16 thousand_a(1000, 'a');
    426   string16 ten_thousand_a(10000, 'a');
    427   string16 hundred_thousand_a(100000, 'a');
    428   string16 million_a(1000000, 'a');
    429 
    430   size_t number_of_as = 156;
    431   string16 long_string_end(
    432       data_scheme + string16(number_of_as, 'a') + kEllipsisStr);
    433   UTF16Testcase testcases_end[] = {
    434      {data_scheme + ten_a,              data_scheme + ten_a},
    435      {data_scheme + hundred_a,          data_scheme + hundred_a},
    436      {data_scheme + thousand_a,         long_string_end},
    437      {data_scheme + ten_thousand_a,     long_string_end},
    438      {data_scheme + hundred_thousand_a, long_string_end},
    439      {data_scheme + million_a,          long_string_end},
    440   };
    441 
    442   const gfx::Font font;
    443   int ellipsis_width = font.GetStringWidth(kEllipsisStr);
    444   for (size_t i = 0; i < arraysize(testcases_end); ++i) {
    445     // Compare sizes rather than actual contents because if the test fails,
    446     // output is rather long.
    447     EXPECT_EQ(testcases_end[i].output.size(),
    448               ElideText(testcases_end[i].input, font,
    449                         font.GetStringWidth(testcases_end[i].output),
    450                         ELIDE_AT_END).size());
    451     EXPECT_EQ(kEllipsisStr,
    452               ElideText(testcases_end[i].input, font, ellipsis_width,
    453                         ELIDE_AT_END));
    454   }
    455 
    456   size_t number_of_trailing_as = (data_scheme_length + number_of_as) / 2;
    457   string16 long_string_middle(data_scheme +
    458       string16(number_of_as - number_of_trailing_as, 'a') + kEllipsisStr +
    459       string16(number_of_trailing_as, 'a'));
    460   UTF16Testcase testcases_middle[] = {
    461      {data_scheme + ten_a,              data_scheme + ten_a},
    462      {data_scheme + hundred_a,          data_scheme + hundred_a},
    463      {data_scheme + thousand_a,         long_string_middle},
    464      {data_scheme + ten_thousand_a,     long_string_middle},
    465      {data_scheme + hundred_thousand_a, long_string_middle},
    466      {data_scheme + million_a,          long_string_middle},
    467   };
    468 
    469   for (size_t i = 0; i < arraysize(testcases_middle); ++i) {
    470     // Compare sizes rather than actual contents because if the test fails,
    471     // output is rather long.
    472     EXPECT_EQ(testcases_middle[i].output.size(),
    473               ElideText(testcases_middle[i].input, font,
    474                         font.GetStringWidth(testcases_middle[i].output),
    475                         ELIDE_AT_END).size());
    476     EXPECT_EQ(kEllipsisStr,
    477               ElideText(testcases_middle[i].input, font, ellipsis_width,
    478                         ELIDE_AT_END));
    479   }
    480 }
    481 
    482 // Verifies display_url is set correctly.
    483 TEST(TextEliderTest, SortedDisplayURL) {
    484   SortedDisplayURL d_url(GURL("http://www.google.com"), std::string());
    485   EXPECT_EQ("www.google.com", UTF16ToASCII(d_url.display_url()));
    486 }
    487 
    488 // Verifies DisplayURL::Compare works correctly.
    489 TEST(TextEliderTest, SortedDisplayURLCompare) {
    490   UErrorCode create_status = U_ZERO_ERROR;
    491   scoped_ptr<icu::Collator> collator(
    492       icu::Collator::createInstance(create_status));
    493   if (!U_SUCCESS(create_status))
    494     return;
    495 
    496   TestData tests[] = {
    497     // IDN comparison. Hosts equal, so compares on path.
    498     { "http://xn--1lq90i.cn/a", "http://xn--1lq90i.cn/b", -1},
    499 
    500     // Because the host and after host match, this compares the full url.
    501     { "http://www.x/b", "http://x/b", -1 },
    502 
    503     // Because the host and after host match, this compares the full url.
    504     { "http://www.a:1/b", "http://a:1/b", 1 },
    505 
    506     // The hosts match, so these end up comparing on the after host portion.
    507     { "http://www.x:0/b", "http://x:1/b", -1 },
    508     { "http://www.x/a", "http://x/b", -1 },
    509     { "http://x/b", "http://www.x/a", 1 },
    510 
    511     // Trivial Equality.
    512     { "http://a/", "http://a/", 0 },
    513 
    514     // Compares just hosts.
    515     { "http://www.a/", "http://b/", -1 },
    516   };
    517 
    518   for (size_t i = 0; i < arraysize(tests); ++i) {
    519     SortedDisplayURL url1(GURL(tests[i].a), std::string());
    520     SortedDisplayURL url2(GURL(tests[i].b), std::string());
    521     EXPECT_EQ(tests[i].compare_result, url1.Compare(url2, collator.get()));
    522     EXPECT_EQ(-tests[i].compare_result, url2.Compare(url1, collator.get()));
    523   }
    524 }
    525 
    526 TEST(TextEliderTest, ElideString) {
    527   struct TestData {
    528     const char* input;
    529     int max_len;
    530     bool result;
    531     const char* output;
    532   } cases[] = {
    533     { "Hello", 0, true, "" },
    534     { "", 0, false, "" },
    535     { "Hello, my name is Tom", 1, true, "H" },
    536     { "Hello, my name is Tom", 2, true, "He" },
    537     { "Hello, my name is Tom", 3, true, "H.m" },
    538     { "Hello, my name is Tom", 4, true, "H..m" },
    539     { "Hello, my name is Tom", 5, true, "H...m" },
    540     { "Hello, my name is Tom", 6, true, "He...m" },
    541     { "Hello, my name is Tom", 7, true, "He...om" },
    542     { "Hello, my name is Tom", 10, true, "Hell...Tom" },
    543     { "Hello, my name is Tom", 100, false, "Hello, my name is Tom" }
    544   };
    545   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    546     string16 output;
    547     EXPECT_EQ(cases[i].result,
    548               ElideString(UTF8ToUTF16(cases[i].input),
    549                           cases[i].max_len, &output));
    550     EXPECT_EQ(cases[i].output, UTF16ToUTF8(output));
    551   }
    552 }
    553 
    554 TEST(TextEliderTest, ElideRectangleText) {
    555   const gfx::Font font;
    556   const int line_height = font.GetHeight();
    557   const int test_width = font.GetStringWidth(ASCIIToUTF16("Test"));
    558 
    559   struct TestData {
    560     const char* input;
    561     int available_pixel_width;
    562     int available_pixel_height;
    563     bool truncated_y;
    564     const char* output;
    565   } cases[] = {
    566     { "", 0, 0, false, NULL },
    567     { "", 1, 1, false, NULL },
    568     { "Test", test_width, 0, true, NULL },
    569     { "Test", test_width, 1, false, "Test" },
    570     { "Test", test_width, line_height, false, "Test" },
    571     { "Test Test", test_width, line_height, true, "Test" },
    572     { "Test Test", test_width, line_height + 1, false, "Test|Test" },
    573     { "Test Test", test_width, line_height * 2, false, "Test|Test" },
    574     { "Test Test", test_width, line_height * 3, false, "Test|Test" },
    575     { "Test Test", test_width * 2, line_height * 2, false, "Test|Test" },
    576     { "Test Test", test_width * 3, line_height, false, "Test Test" },
    577     { "Test\nTest", test_width * 3, line_height * 2, false, "Test|Test" },
    578     { "Te\nst Te", test_width, line_height * 3, false, "Te|st|Te" },
    579     { "\nTest", test_width, line_height * 2, false, "|Test" },
    580     { "\nTest", test_width, line_height, true, "" },
    581     { "\n\nTest", test_width, line_height * 3, false, "||Test" },
    582     { "\n\nTest", test_width, line_height * 2, true, "|" },
    583     { "Test\n", 2 * test_width, line_height * 5, false, "Test|" },
    584     { "Test\n\n", 2 * test_width, line_height * 5, false, "Test||" },
    585     { "Test\n\n\n", 2 * test_width, line_height * 5, false, "Test|||" },
    586     { "Test\nTest\n\n", 2 * test_width, line_height * 5, false, "Test|Test||" },
    587     { "Test\n\nTest\n", 2 * test_width, line_height * 5, false, "Test||Test|" },
    588     { "Test\n\n\nTest", 2 * test_width, line_height * 5, false, "Test|||Test" },
    589     { "Te ", test_width, line_height, false, "Te" },
    590     { "Te  Te Test", test_width, 3 * line_height, false, "Te|Te|Test" },
    591   };
    592 
    593   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    594     std::vector<string16> lines;
    595     EXPECT_EQ(cases[i].truncated_y ? INSUFFICIENT_SPACE_VERTICAL : 0,
    596               ElideRectangleText(UTF8ToUTF16(cases[i].input),
    597                                  font,
    598                                  cases[i].available_pixel_width,
    599                                  cases[i].available_pixel_height,
    600                                  TRUNCATE_LONG_WORDS,
    601                                  &lines));
    602     if (cases[i].output) {
    603       const std::string result = UTF16ToUTF8(JoinString(lines, '|'));
    604       EXPECT_EQ(cases[i].output, result) << "Case " << i << " failed!";
    605     } else {
    606       EXPECT_TRUE(lines.empty()) << "Case " << i << " failed!";
    607     }
    608   }
    609 }
    610 
    611 TEST(TextEliderTest, ElideRectangleTextPunctuation) {
    612   const gfx::Font font;
    613   const int line_height = font.GetHeight();
    614   const int test_width = font.GetStringWidth(ASCIIToUTF16("Test"));
    615   const int test_t_width = font.GetStringWidth(ASCIIToUTF16("Test T"));
    616 
    617   struct TestData {
    618     const char* input;
    619     int available_pixel_width;
    620     int available_pixel_height;
    621     bool wrap_words;
    622     bool truncated_x;
    623     const char* output;
    624   } cases[] = {
    625     { "Test T.", test_t_width, line_height * 2, false, false, "Test|T." },
    626     { "Test T ?", test_t_width, line_height * 2, false, false, "Test|T ?" },
    627     { "Test. Test", test_width, line_height * 3, false, true, "Test|Test" },
    628     { "Test. Test", test_width, line_height * 3, true, false, "Test|.|Test" },
    629   };
    630 
    631   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    632     std::vector<string16> lines;
    633     const WordWrapBehavior wrap_behavior =
    634         (cases[i].wrap_words ? WRAP_LONG_WORDS : TRUNCATE_LONG_WORDS);
    635     EXPECT_EQ(cases[i].truncated_x ? INSUFFICIENT_SPACE_HORIZONTAL : 0,
    636               ElideRectangleText(UTF8ToUTF16(cases[i].input),
    637                                  font,
    638                                  cases[i].available_pixel_width,
    639                                  cases[i].available_pixel_height,
    640                                  wrap_behavior,
    641                                  &lines));
    642     if (cases[i].output) {
    643       const std::string result = UTF16ToUTF8(JoinString(lines, '|'));
    644       EXPECT_EQ(cases[i].output, result) << "Case " << i << " failed!";
    645     } else {
    646       EXPECT_TRUE(lines.empty()) << "Case " << i << " failed!";
    647     }
    648   }
    649 }
    650 
    651 TEST(TextEliderTest, ElideRectangleTextLongWords) {
    652   const gfx::Font font;
    653   const int kAvailableHeight = 1000;
    654   const string16 kElidedTesting = UTF8ToUTF16(std::string("Tes") + kEllipsis);
    655   const int elided_width = font.GetStringWidth(kElidedTesting);
    656   const int test_width = font.GetStringWidth(ASCIIToUTF16("Test"));
    657 
    658   struct TestData {
    659     const char* input;
    660     int available_pixel_width;
    661     WordWrapBehavior wrap_behavior;
    662     bool truncated_x;
    663     const char* output;
    664   } cases[] = {
    665     { "Testing", test_width, IGNORE_LONG_WORDS, false, "Testing" },
    666     { "X Testing", test_width, IGNORE_LONG_WORDS, false, "X|Testing" },
    667     { "Test Testing", test_width, IGNORE_LONG_WORDS, false, "Test|Testing" },
    668     { "Test\nTesting", test_width, IGNORE_LONG_WORDS, false, "Test|Testing" },
    669     { "Test Tests ", test_width, IGNORE_LONG_WORDS, false, "Test|Tests" },
    670     { "Test Tests T", test_width, IGNORE_LONG_WORDS, false, "Test|Tests|T" },
    671 
    672     { "Testing", elided_width, ELIDE_LONG_WORDS, true, "Tes..." },
    673     { "X Testing", elided_width, ELIDE_LONG_WORDS, true, "X|Tes..." },
    674     { "Test Testing", elided_width, ELIDE_LONG_WORDS, true, "Test|Tes..." },
    675     { "Test\nTesting", elided_width, ELIDE_LONG_WORDS, true, "Test|Tes..." },
    676 
    677     { "Testing", test_width, TRUNCATE_LONG_WORDS, true, "Test" },
    678     { "X Testing", test_width, TRUNCATE_LONG_WORDS, true, "X|Test" },
    679     { "Test Testing", test_width, TRUNCATE_LONG_WORDS, true, "Test|Test" },
    680     { "Test\nTesting", test_width, TRUNCATE_LONG_WORDS, true, "Test|Test" },
    681     { "Test Tests ", test_width, TRUNCATE_LONG_WORDS, true, "Test|Test" },
    682     { "Test Tests T", test_width, TRUNCATE_LONG_WORDS, true, "Test|Test|T" },
    683 
    684     { "Testing", test_width, WRAP_LONG_WORDS, false, "Test|ing" },
    685     { "X Testing", test_width, WRAP_LONG_WORDS, false, "X|Test|ing" },
    686     { "Test Testing", test_width, WRAP_LONG_WORDS, false, "Test|Test|ing" },
    687     { "Test\nTesting", test_width, WRAP_LONG_WORDS, false, "Test|Test|ing" },
    688     { "Test Tests ", test_width, WRAP_LONG_WORDS, false, "Test|Test|s" },
    689     { "Test Tests T", test_width, WRAP_LONG_WORDS, false, "Test|Test|s T" },
    690     { "TestTestTest", test_width, WRAP_LONG_WORDS, false, "Test|Test|Test" },
    691     { "TestTestTestT", test_width, WRAP_LONG_WORDS, false, "Test|Test|Test|T" },
    692   };
    693 
    694   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    695     std::vector<string16> lines;
    696     EXPECT_EQ(cases[i].truncated_x ? INSUFFICIENT_SPACE_HORIZONTAL : 0,
    697               ElideRectangleText(UTF8ToUTF16(cases[i].input),
    698                                  font,
    699                                  cases[i].available_pixel_width,
    700                                  kAvailableHeight,
    701                                  cases[i].wrap_behavior,
    702                                  &lines));
    703     std::string expected_output(cases[i].output);
    704     ReplaceSubstringsAfterOffset(&expected_output, 0, "...", kEllipsis);
    705     const std::string result = UTF16ToUTF8(JoinString(lines, '|'));
    706     EXPECT_EQ(expected_output, result) << "Case " << i << " failed!";
    707   }
    708 }
    709 
    710 TEST(TextEliderTest, ElideRectangleString) {
    711   struct TestData {
    712     const char* input;
    713     int max_rows;
    714     int max_cols;
    715     bool result;
    716     const char* output;
    717   } cases[] = {
    718     { "", 0, 0, false, "" },
    719     { "", 1, 1, false, "" },
    720     { "Hi, my name is\nTom", 0, 0,  true,  "..." },
    721     { "Hi, my name is\nTom", 1, 0,  true,  "\n..." },
    722     { "Hi, my name is\nTom", 0, 1,  true,  "..." },
    723     { "Hi, my name is\nTom", 1, 1,  true,  "H\n..." },
    724     { "Hi, my name is\nTom", 2, 1,  true,  "H\ni\n..." },
    725     { "Hi, my name is\nTom", 3, 1,  true,  "H\ni\n,\n..." },
    726     { "Hi, my name is\nTom", 4, 1,  true,  "H\ni\n,\n \n..." },
    727     { "Hi, my name is\nTom", 5, 1,  true,  "H\ni\n,\n \nm\n..." },
    728     { "Hi, my name is\nTom", 0, 2,  true,  "..." },
    729     { "Hi, my name is\nTom", 1, 2,  true,  "Hi\n..." },
    730     { "Hi, my name is\nTom", 2, 2,  true,  "Hi\n, \n..." },
    731     { "Hi, my name is\nTom", 3, 2,  true,  "Hi\n, \nmy\n..." },
    732     { "Hi, my name is\nTom", 4, 2,  true,  "Hi\n, \nmy\n n\n..." },
    733     { "Hi, my name is\nTom", 5, 2,  true,  "Hi\n, \nmy\n n\nam\n..." },
    734     { "Hi, my name is\nTom", 0, 3,  true,  "..." },
    735     { "Hi, my name is\nTom", 1, 3,  true,  "Hi,\n..." },
    736     { "Hi, my name is\nTom", 2, 3,  true,  "Hi,\n my\n..." },
    737     { "Hi, my name is\nTom", 3, 3,  true,  "Hi,\n my\n na\n..." },
    738     { "Hi, my name is\nTom", 4, 3,  true,  "Hi,\n my\n na\nme \n..." },
    739     { "Hi, my name is\nTom", 5, 3,  true,  "Hi,\n my\n na\nme \nis\n..." },
    740     { "Hi, my name is\nTom", 1, 4,  true,  "Hi, \n..." },
    741     { "Hi, my name is\nTom", 2, 4,  true,  "Hi, \nmy n\n..." },
    742     { "Hi, my name is\nTom", 3, 4,  true,  "Hi, \nmy n\name \n..." },
    743     { "Hi, my name is\nTom", 4, 4,  true,  "Hi, \nmy n\name \nis\n..." },
    744     { "Hi, my name is\nTom", 5, 4,  false, "Hi, \nmy n\name \nis\nTom" },
    745     { "Hi, my name is\nTom", 1, 5,  true,  "Hi, \n..." },
    746     { "Hi, my name is\nTom", 2, 5,  true,  "Hi, \nmy na\n..." },
    747     { "Hi, my name is\nTom", 3, 5,  true,  "Hi, \nmy na\nme \n..." },
    748     { "Hi, my name is\nTom", 4, 5,  true,  "Hi, \nmy na\nme \nis\n..." },
    749     { "Hi, my name is\nTom", 5, 5,  false, "Hi, \nmy na\nme \nis\nTom" },
    750     { "Hi, my name is\nTom", 1, 6,  true,  "Hi, \n..." },
    751     { "Hi, my name is\nTom", 2, 6,  true,  "Hi, \nmy \n..." },
    752     { "Hi, my name is\nTom", 3, 6,  true,  "Hi, \nmy \nname \n..." },
    753     { "Hi, my name is\nTom", 4, 6,  true,  "Hi, \nmy \nname \nis\n..." },
    754     { "Hi, my name is\nTom", 5, 6,  false, "Hi, \nmy \nname \nis\nTom" },
    755     { "Hi, my name is\nTom", 1, 7,  true,  "Hi, \n..." },
    756     { "Hi, my name is\nTom", 2, 7,  true,  "Hi, \nmy \n..." },
    757     { "Hi, my name is\nTom", 3, 7,  true,  "Hi, \nmy \nname \n..." },
    758     { "Hi, my name is\nTom", 4, 7,  true,  "Hi, \nmy \nname \nis\n..." },
    759     { "Hi, my name is\nTom", 5, 7,  false, "Hi, \nmy \nname \nis\nTom" },
    760     { "Hi, my name is\nTom", 1, 8,  true,  "Hi, my \n..." },
    761     { "Hi, my name is\nTom", 2, 8,  true,  "Hi, my \nname \n..." },
    762     { "Hi, my name is\nTom", 3, 8,  true,  "Hi, my \nname \nis\n..." },
    763     { "Hi, my name is\nTom", 4, 8,  false, "Hi, my \nname \nis\nTom" },
    764     { "Hi, my name is\nTom", 1, 9,  true,  "Hi, my \n..." },
    765     { "Hi, my name is\nTom", 2, 9,  true,  "Hi, my \nname is\n..." },
    766     { "Hi, my name is\nTom", 3, 9,  false, "Hi, my \nname is\nTom" },
    767     { "Hi, my name is\nTom", 1, 10, true,  "Hi, my \n..." },
    768     { "Hi, my name is\nTom", 2, 10, true,  "Hi, my \nname is\n..." },
    769     { "Hi, my name is\nTom", 3, 10, false, "Hi, my \nname is\nTom" },
    770     { "Hi, my name is\nTom", 1, 11, true,  "Hi, my \n..." },
    771     { "Hi, my name is\nTom", 2, 11, true,  "Hi, my \nname is\n..." },
    772     { "Hi, my name is\nTom", 3, 11, false, "Hi, my \nname is\nTom" },
    773     { "Hi, my name is\nTom", 1, 12, true,  "Hi, my \n..." },
    774     { "Hi, my name is\nTom", 2, 12, true,  "Hi, my \nname is\n..." },
    775     { "Hi, my name is\nTom", 3, 12, false, "Hi, my \nname is\nTom" },
    776     { "Hi, my name is\nTom", 1, 13, true,  "Hi, my name \n..." },
    777     { "Hi, my name is\nTom", 2, 13, true,  "Hi, my name \nis\n..." },
    778     { "Hi, my name is\nTom", 3, 13, false, "Hi, my name \nis\nTom" },
    779     { "Hi, my name is\nTom", 1, 20, true,  "Hi, my name is\n..." },
    780     { "Hi, my name is\nTom", 2, 20, false, "Hi, my name is\nTom" },
    781     { "Hi, my name is Tom",  1, 40, false, "Hi, my name is Tom" },
    782   };
    783   string16 output;
    784   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    785     EXPECT_EQ(cases[i].result,
    786               ElideRectangleString(UTF8ToUTF16(cases[i].input),
    787                                    cases[i].max_rows, cases[i].max_cols,
    788                                    true, &output));
    789     EXPECT_EQ(cases[i].output, UTF16ToUTF8(output));
    790   }
    791 }
    792 
    793 TEST(TextEliderTest, ElideRectangleStringNotStrict) {
    794   struct TestData {
    795     const char* input;
    796     int max_rows;
    797     int max_cols;
    798     bool result;
    799     const char* output;
    800   } cases[] = {
    801     { "", 0, 0, false, "" },
    802     { "", 1, 1, false, "" },
    803     { "Hi, my name_is\nDick", 0, 0,  true,  "..." },
    804     { "Hi, my name_is\nDick", 1, 0,  true,  "\n..." },
    805     { "Hi, my name_is\nDick", 0, 1,  true,  "..." },
    806     { "Hi, my name_is\nDick", 1, 1,  true,  "H\n..." },
    807     { "Hi, my name_is\nDick", 2, 1,  true,  "H\ni\n..." },
    808     { "Hi, my name_is\nDick", 3, 1,  true,  "H\ni\n,\n..." },
    809     { "Hi, my name_is\nDick", 4, 1,  true,  "H\ni\n,\n \n..." },
    810     { "Hi, my name_is\nDick", 5, 1,  true,  "H\ni\n,\n \nm\n..." },
    811     { "Hi, my name_is\nDick", 0, 2,  true,  "..." },
    812     { "Hi, my name_is\nDick", 1, 2,  true,  "Hi\n..." },
    813     { "Hi, my name_is\nDick", 2, 2,  true,  "Hi\n, \n..." },
    814     { "Hi, my name_is\nDick", 3, 2,  true,  "Hi\n, \nmy\n..." },
    815     { "Hi, my name_is\nDick", 4, 2,  true,  "Hi\n, \nmy\n n\n..." },
    816     { "Hi, my name_is\nDick", 5, 2,  true,  "Hi\n, \nmy\n n\nam\n..." },
    817     { "Hi, my name_is\nDick", 0, 3,  true,  "..." },
    818     { "Hi, my name_is\nDick", 1, 3,  true,  "Hi,\n..." },
    819     { "Hi, my name_is\nDick", 2, 3,  true,  "Hi,\n my\n..." },
    820     { "Hi, my name_is\nDick", 3, 3,  true,  "Hi,\n my\n na\n..." },
    821     { "Hi, my name_is\nDick", 4, 3,  true,  "Hi,\n my\n na\nme_\n..." },
    822     { "Hi, my name_is\nDick", 5, 3,  true,  "Hi,\n my\n na\nme_\nis\n..." },
    823     { "Hi, my name_is\nDick", 1, 4,  true,  "Hi, ..." },
    824     { "Hi, my name_is\nDick", 2, 4,  true,  "Hi, my n\n..." },
    825     { "Hi, my name_is\nDick", 3, 4,  true,  "Hi, my n\name_\n..." },
    826     { "Hi, my name_is\nDick", 4, 4,  true,  "Hi, my n\name_\nis\n..." },
    827     { "Hi, my name_is\nDick", 5, 4,  false, "Hi, my n\name_\nis\nDick" },
    828     { "Hi, my name_is\nDick", 1, 5,  true,  "Hi, ..." },
    829     { "Hi, my name_is\nDick", 2, 5,  true,  "Hi, my na\n..." },
    830     { "Hi, my name_is\nDick", 3, 5,  true,  "Hi, my na\nme_is\n..." },
    831     { "Hi, my name_is\nDick", 4, 5,  true,  "Hi, my na\nme_is\n\n..." },
    832     { "Hi, my name_is\nDick", 5, 5,  false, "Hi, my na\nme_is\n\nDick" },
    833     { "Hi, my name_is\nDick", 1, 6,  true,  "Hi, ..." },
    834     { "Hi, my name_is\nDick", 2, 6,  true,  "Hi, my nam\n..." },
    835     { "Hi, my name_is\nDick", 3, 6,  true,  "Hi, my nam\ne_is\n..." },
    836     { "Hi, my name_is\nDick", 4, 6,  false, "Hi, my nam\ne_is\nDick" },
    837     { "Hi, my name_is\nDick", 5, 6,  false, "Hi, my nam\ne_is\nDick" },
    838     { "Hi, my name_is\nDick", 1, 7,  true,  "Hi, ..." },
    839     { "Hi, my name_is\nDick", 2, 7,  true,  "Hi, my name\n..." },
    840     { "Hi, my name_is\nDick", 3, 7,  true,  "Hi, my name\n_is\n..." },
    841     { "Hi, my name_is\nDick", 4, 7,  false, "Hi, my name\n_is\nDick" },
    842     { "Hi, my name_is\nDick", 5, 7,  false, "Hi, my name\n_is\nDick" },
    843     { "Hi, my name_is\nDick", 1, 8,  true,  "Hi, my n\n..." },
    844     { "Hi, my name_is\nDick", 2, 8,  true,  "Hi, my n\name_is\n..." },
    845     { "Hi, my name_is\nDick", 3, 8,  false, "Hi, my n\name_is\nDick" },
    846     { "Hi, my name_is\nDick", 1, 9,  true,  "Hi, my ..." },
    847     { "Hi, my name_is\nDick", 2, 9,  true,  "Hi, my name_is\n..." },
    848     { "Hi, my name_is\nDick", 3, 9,  false, "Hi, my name_is\nDick" },
    849     { "Hi, my name_is\nDick", 1, 10, true,  "Hi, my ..." },
    850     { "Hi, my name_is\nDick", 2, 10, true,  "Hi, my name_is\n..." },
    851     { "Hi, my name_is\nDick", 3, 10, false, "Hi, my name_is\nDick" },
    852     { "Hi, my name_is\nDick", 1, 11, true,  "Hi, my ..." },
    853     { "Hi, my name_is\nDick", 2, 11, true,  "Hi, my name_is\n..." },
    854     { "Hi, my name_is\nDick", 3, 11, false, "Hi, my name_is\nDick" },
    855     { "Hi, my name_is\nDick", 1, 12, true,  "Hi, my ..." },
    856     { "Hi, my name_is\nDick", 2, 12, true,  "Hi, my name_is\n..." },
    857     { "Hi, my name_is\nDick", 3, 12, false, "Hi, my name_is\nDick" },
    858     { "Hi, my name_is\nDick", 1, 13, true,  "Hi, my ..." },
    859     { "Hi, my name_is\nDick", 2, 13, true,  "Hi, my name_is\n..." },
    860     { "Hi, my name_is\nDick", 3, 13, false, "Hi, my name_is\nDick" },
    861     { "Hi, my name_is\nDick", 1, 20, true,  "Hi, my name_is\n..." },
    862     { "Hi, my name_is\nDick", 2, 20, false, "Hi, my name_is\nDick" },
    863     { "Hi, my name_is Dick",  1, 40, false, "Hi, my name_is Dick" },
    864   };
    865   string16 output;
    866   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    867     EXPECT_EQ(cases[i].result,
    868               ElideRectangleString(UTF8ToUTF16(cases[i].input),
    869                                    cases[i].max_rows, cases[i].max_cols,
    870                                    false, &output));
    871     EXPECT_EQ(cases[i].output, UTF16ToUTF8(output));
    872   }
    873 }
    874 
    875 TEST(TextEliderTest, ElideRectangleWide16) {
    876   // Two greek words separated by space.
    877   const string16 str(WideToUTF16(
    878       L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
    879       L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2"));
    880   const string16 out1(WideToUTF16(
    881       L"\x03a0\x03b1\x03b3\x03ba\n"
    882       L"\x03cc\x03c3\x03bc\x03b9\n"
    883       L"..."));
    884   const string16 out2(WideToUTF16(
    885       L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9\x03bf\x03c2\x0020\n"
    886       L"\x0399\x03c3\x03c4\x03cc\x03c2"));
    887   string16 output;
    888   EXPECT_TRUE(ElideRectangleString(str, 2, 4, true, &output));
    889   EXPECT_EQ(out1, output);
    890   EXPECT_FALSE(ElideRectangleString(str, 2, 12, true, &output));
    891   EXPECT_EQ(out2, output);
    892 }
    893 
    894 TEST(TextEliderTest, ElideRectangleWide32) {
    895   // Four U+1D49C MATHEMATICAL SCRIPT CAPITAL A followed by space "aaaaa".
    896   const string16 str(UTF8ToUTF16(
    897       "\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C"
    898       " aaaaa"));
    899   const string16 out(UTF8ToUTF16(
    900       "\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\n"
    901       "\xF0\x9D\x92\x9C \naaa\n..."));
    902   string16 output;
    903   EXPECT_TRUE(ElideRectangleString(str, 3, 3, true, &output));
    904   EXPECT_EQ(out, output);
    905 }
    906 
    907 TEST(TextEliderTest, TruncateString) {
    908   string16 string = ASCIIToUTF16("foooooey    bxxxar baz");
    909 
    910   // Make sure it doesn't modify the string if length > string length.
    911   EXPECT_EQ(string, TruncateString(string, 100));
    912 
    913   // Test no characters.
    914   EXPECT_EQ(L"", UTF16ToWide(TruncateString(string, 0)));
    915 
    916   // Test 1 character.
    917   EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(string, 1)));
    918 
    919   // Test adds ... at right spot when there is enough room to break at a
    920   // word boundary.
    921   EXPECT_EQ(L"foooooey\x2026", UTF16ToWide(TruncateString(string, 14)));
    922 
    923   // Test adds ... at right spot when there is not enough space in first word.
    924   EXPECT_EQ(L"f\x2026", UTF16ToWide(TruncateString(string, 2)));
    925 
    926   // Test adds ... at right spot when there is not enough room to break at a
    927   // word boundary.
    928   EXPECT_EQ(L"foooooey\x2026", UTF16ToWide(TruncateString(string, 11)));
    929 
    930   // Test completely truncates string if break is on initial whitespace.
    931   EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(ASCIIToUTF16("   "), 2)));
    932 }
    933 
    934 }  // namespace ui
    935