1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "app/text_elider.h"
6 #include "base/file_path.h"
7 #include "base/i18n/rtl.h"
8 #include "base/scoped_ptr.h"
9 #include "base/string_util.h"
10 #include "base/utf_string_conversions.h"
12 #include "googleurl/src/gurl.h"
13 #include "testing/gtest/include/gtest/gtest.h"
17 const wchar_t kEllipsis
[] = L
"\x2026";
20 const std::string input
;
21 const std::wstring output
;
25 const FilePath::StringType input
;
26 const std::wstring output
;
30 const std::wstring input
;
31 const std::wstring output
;
37 const int compare_result
;
40 void RunTest(Testcase
* testcases
, size_t num_testcases
) {
41 static const gfx::Font font
;
42 for (size_t i
= 0; i
< num_testcases
; ++i
) {
43 const GURL
url(testcases
[i
].input
);
44 // Should we test with non-empty language list?
45 // That's kinda redundant with net_util_unittests.
46 EXPECT_EQ(testcases
[i
].output
,
47 ElideUrl(url
, font
, font
.GetStringWidth(testcases
[i
].output
),
54 // Test eliding of commonplace URLs.
55 TEST(TextEliderTest
, TestGeneralEliding
) {
56 const std::wstring
kEllipsisStr(kEllipsis
);
57 Testcase testcases
[] = {
58 {"http://www.google.com/intl/en/ads/",
59 L
"www.google.com/intl/en/ads/"},
60 {"http://www.google.com/intl/en/ads/", L
"www.google.com/intl/en/ads/"},
61 // TODO(port): make this test case work on mac.
62 #if !defined(OS_MACOSX)
63 {"http://www.google.com/intl/en/ads/",
64 L
"google.com/intl/" + kEllipsisStr
+ L
"/ads/"},
66 {"http://www.google.com/intl/en/ads/",
67 L
"google.com/" + kEllipsisStr
+ L
"/ads/"},
68 {"http://www.google.com/intl/en/ads/", L
"google.com/" + kEllipsisStr
},
69 {"http://www.google.com/intl/en/ads/", L
"goog" + kEllipsisStr
},
70 {"https://subdomain.foo.com/bar/filename.html",
71 L
"subdomain.foo.com/bar/filename.html"},
72 {"https://subdomain.foo.com/bar/filename.html",
73 L
"subdomain.foo.com/" + kEllipsisStr
+ L
"/filename.html"},
74 {"http://subdomain.foo.com/bar/filename.html",
75 kEllipsisStr
+ L
"foo.com/" + kEllipsisStr
+ L
"/filename.html"},
76 {"http://www.google.com/intl/en/ads/?aLongQueryWhichIsNotRequired",
77 L
"www.google.com/intl/en/ads/?aLongQ" + kEllipsisStr
},
80 RunTest(testcases
, arraysize(testcases
));
83 // Test eliding of empty strings, URLs with ports, passwords, queries, etc.
84 TEST(TextEliderTest
, TestMoreEliding
) {
85 const std::wstring
kEllipsisStr(kEllipsis
);
86 Testcase testcases
[] = {
87 {"http://www.google.com/foo?bar", L
"www.google.com/foo?bar"},
88 {"http://xyz.google.com/foo?bar", L
"xyz.google.com/foo?" + kEllipsisStr
},
89 {"http://xyz.google.com/foo?bar", L
"xyz.google.com/foo" + kEllipsisStr
},
90 {"http://xyz.google.com/foo?bar", L
"xyz.google.com/fo" + kEllipsisStr
},
91 {"http://a.b.com/pathname/c?d", L
"a.b.com/" + kEllipsisStr
+ L
"/c?d"},
93 {"http://foo.bar..example.com...hello/test/filename.html",
94 L
"foo.bar..example.com...hello/" + kEllipsisStr
+ L
"/filename.html"},
95 {"http://foo.bar../", L
"foo.bar.."},
96 {"http://xn--1lq90i.cn/foo", L
"\x5317\x4eac.cn/foo"},
97 {"http://me:mypass@secrethost.com:99/foo?bar#baz",
98 L
"secrethost.com:99/foo?bar#baz"},
99 {"http://me:mypass@ss%xxfdsf.com/foo", L
"ss%25xxfdsf.com/foo"},
100 {"mailto:elgoato@elgoato.com", L
"mailto:elgoato@elgoato.com"},
101 {"javascript:click(0)", L
"javascript:click(0)"},
102 {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename",
103 L
"chess.eecs.berkeley.edu:4430/login/arbitfilename"},
104 {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename",
105 kEllipsisStr
+ L
"berkeley.edu:4430/" + kEllipsisStr
+ L
"/arbitfilename"},
108 {"http://www/%E4%BD%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0",
109 L
"www/\x4f60\x597d?q=\x4f60\x597d#\x4f60"},
111 // Invalid unescaping for path. The ref will always be valid UTF-8. We don't
112 // bother to do too many edge cases, since these are handled by the escaper
114 {"http://www/%E4%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0",
115 L
"www/%E4%A0%E5%A5%BD?q=\x4f60\x597d#\x4f60"},
118 RunTest(testcases
, arraysize(testcases
));
121 // Test eliding of file: URLs.
122 TEST(TextEliderTest
, TestFileURLEliding
) {
123 const std::wstring
kEllipsisStr(kEllipsis
);
124 Testcase testcases
[] = {
125 {"file:///C:/path1/path2/path3/filename",
126 L
"file:///C:/path1/path2/path3/filename"},
127 {"file:///C:/path1/path2/path3/filename",
128 L
"C:/path1/path2/path3/filename"},
129 // GURL parses "file:///C:path" differently on windows than it does on posix.
131 {"file:///C:path1/path2/path3/filename",
132 L
"C:/path1/path2/" + kEllipsisStr
+ L
"/filename"},
133 {"file:///C:path1/path2/path3/filename",
134 L
"C:/path1/" + kEllipsisStr
+ L
"/filename"},
135 {"file:///C:path1/path2/path3/filename",
136 L
"C:/" + kEllipsisStr
+ L
"/filename"},
138 {"file://filer/foo/bar/file", L
"filer/foo/bar/file"},
139 {"file://filer/foo/bar/file", L
"filer/foo/" + kEllipsisStr
+ L
"/file"},
140 {"file://filer/foo/bar/file", L
"filer/" + kEllipsisStr
+ L
"/file"},
143 RunTest(testcases
, arraysize(testcases
));
146 TEST(TextEliderTest
, TestFilenameEliding
) {
147 const std::wstring
kEllipsisStr(kEllipsis
);
148 const FilePath::StringType kPathSeparator
=
149 FilePath::StringType().append(1, FilePath::kSeparators
[0]);
151 FileTestcase testcases
[] = {
152 {FILE_PATH_LITERAL(""), L
""},
153 {FILE_PATH_LITERAL("."), L
"."},
154 {FILE_PATH_LITERAL("filename.exe"), L
"filename.exe"},
155 {FILE_PATH_LITERAL(".longext"), L
".longext"},
156 {FILE_PATH_LITERAL("pie"), L
"pie"},
157 {FILE_PATH_LITERAL("c:") + kPathSeparator
+ FILE_PATH_LITERAL("path") +
158 kPathSeparator
+ FILE_PATH_LITERAL("filename.pie"),
160 {FILE_PATH_LITERAL("c:") + kPathSeparator
+ FILE_PATH_LITERAL("path") +
161 kPathSeparator
+ FILE_PATH_LITERAL("longfilename.pie"),
162 L
"long" + kEllipsisStr
+ L
".pie"},
163 {FILE_PATH_LITERAL("http://path.com/filename.pie"), L
"filename.pie"},
164 {FILE_PATH_LITERAL("http://path.com/longfilename.pie"),
165 L
"long" + kEllipsisStr
+ L
".pie"},
166 {FILE_PATH_LITERAL("piesmashingtacularpants"), L
"pie" + kEllipsisStr
},
167 {FILE_PATH_LITERAL(".piesmashingtacularpants"), L
".pie" + kEllipsisStr
},
168 {FILE_PATH_LITERAL("cheese."), L
"cheese."},
169 {FILE_PATH_LITERAL("file name.longext"),
170 L
"file" + kEllipsisStr
+ L
".longext"},
171 {FILE_PATH_LITERAL("fil ename.longext"),
172 L
"fil " + kEllipsisStr
+ L
".longext"},
173 {FILE_PATH_LITERAL("filename.longext"),
174 L
"file" + kEllipsisStr
+ L
".longext"},
175 {FILE_PATH_LITERAL("filename.middleext.longext"),
176 L
"filename.mid" + kEllipsisStr
+ L
".longext"}
179 static const gfx::Font font
;
180 for (size_t i
= 0; i
< arraysize(testcases
); ++i
) {
181 FilePath
filepath(testcases
[i
].input
);
182 string16 expected
= WideToUTF16(testcases
[i
].output
);
183 expected
= base::i18n::GetDisplayStringInLTRDirectionality(expected
);
184 EXPECT_EQ(expected
, WideToUTF16(ElideFilename(filepath
,
186 font
.GetStringWidth(testcases
[i
].output
))));
190 TEST(TextEliderTest
, ElideTextLongStrings
) {
191 const std::wstring
kEllipsisStr(kEllipsis
);
192 std::wstring
data_scheme(L
"data:text/plain,");
193 size_t data_scheme_length
= data_scheme
.length();
195 std::wstring
ten_a(10, L
'a');
196 std::wstring
hundred_a(100, L
'a');
197 std::wstring
thousand_a(1000, L
'a');
198 std::wstring
ten_thousand_a(10000, L
'a');
199 std::wstring
hundred_thousand_a(100000, L
'a');
200 std::wstring
million_a(1000000, L
'a');
202 size_t number_of_as
= 156;
203 std::wstring
long_string_end(
204 data_scheme
+ std::wstring(number_of_as
, L
'a') + kEllipsisStr
);
205 WideTestcase testcases_end
[] = {
206 {data_scheme
+ ten_a
, data_scheme
+ ten_a
},
207 {data_scheme
+ hundred_a
, data_scheme
+ hundred_a
},
208 {data_scheme
+ thousand_a
, long_string_end
},
209 {data_scheme
+ ten_thousand_a
, long_string_end
},
210 {data_scheme
+ hundred_thousand_a
, long_string_end
},
211 {data_scheme
+ million_a
, long_string_end
},
214 const gfx::Font font
;
215 int ellipsis_width
= font
.GetStringWidth(kEllipsisStr
);
216 for (size_t i
= 0; i
< arraysize(testcases_end
); ++i
) {
217 // Compare sizes rather than actual contents because if the test fails,
218 // output is rather long.
219 EXPECT_EQ(testcases_end
[i
].output
.size(),
220 ElideText(testcases_end
[i
].input
, font
,
221 font
.GetStringWidth(testcases_end
[i
].output
),
223 EXPECT_EQ(kEllipsisStr
,
224 ElideText(testcases_end
[i
].input
, font
, ellipsis_width
, false));
227 size_t number_of_trailing_as
= (data_scheme_length
+ number_of_as
) / 2;
228 std::wstring
long_string_middle(data_scheme
+
229 std::wstring(number_of_as
- number_of_trailing_as
, L
'a') + kEllipsisStr
+
230 std::wstring(number_of_trailing_as
, L
'a'));
231 WideTestcase testcases_middle
[] = {
232 {data_scheme
+ ten_a
, data_scheme
+ ten_a
},
233 {data_scheme
+ hundred_a
, data_scheme
+ hundred_a
},
234 {data_scheme
+ thousand_a
, long_string_middle
},
235 {data_scheme
+ ten_thousand_a
, long_string_middle
},
236 {data_scheme
+ hundred_thousand_a
, long_string_middle
},
237 {data_scheme
+ million_a
, long_string_middle
},
240 for (size_t i
= 0; i
< arraysize(testcases_middle
); ++i
) {
241 // Compare sizes rather than actual contents because if the test fails,
242 // output is rather long.
243 EXPECT_EQ(testcases_middle
[i
].output
.size(),
244 ElideText(testcases_middle
[i
].input
, font
,
245 font
.GetStringWidth(testcases_middle
[i
].output
),
247 EXPECT_EQ(kEllipsisStr
,
248 ElideText(testcases_middle
[i
].input
, font
, ellipsis_width
,
253 // Verifies display_url is set correctly.
254 TEST(TextEliderTest
, SortedDisplayURL
) {
255 gfx::SortedDisplayURL
d_url(GURL("http://www.google.com"), std::wstring());
256 EXPECT_EQ("www.google.com", UTF16ToASCII(d_url
.display_url()));
259 // Verifies DisplayURL::Compare works correctly.
260 TEST(TextEliderTest
, SortedDisplayURLCompare
) {
261 UErrorCode create_status
= U_ZERO_ERROR
;
262 scoped_ptr
<icu::Collator
> collator(
263 icu::Collator::createInstance(create_status
));
264 if (!U_SUCCESS(create_status
))
268 // IDN comparison. Hosts equal, so compares on path.
269 { "http://xn--1lq90i.cn/a", "http://xn--1lq90i.cn/b", -1},
271 // Because the host and after host match, this compares the full url.
272 { "http://www.x/b", "http://x/b", -1 },
274 // Because the host and after host match, this compares the full url.
275 { "http://www.a:1/b", "http://a:1/b", 1 },
277 // The hosts match, so these end up comparing on the after host portion.
278 { "http://www.x:0/b", "http://x:1/b", -1 },
279 { "http://www.x/a", "http://x/b", -1 },
280 { "http://x/b", "http://www.x/a", 1 },
283 { "http://a/", "http://a/", 0 },
285 // Compares just hosts.
286 { "http://www.a/", "http://b/", -1 },
289 for (size_t i
= 0; i
< arraysize(tests
); ++i
) {
290 gfx::SortedDisplayURL
url1(GURL(tests
[i
].a
), std::wstring());
291 gfx::SortedDisplayURL
url2(GURL(tests
[i
].b
), std::wstring());
292 EXPECT_EQ(tests
[i
].compare_result
, url1
.Compare(url2
, collator
.get()));
293 EXPECT_EQ(-tests
[i
].compare_result
, url2
.Compare(url1
, collator
.get()));