OLD | NEW |
| (Empty) |
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 #include "components/omnibox/browser/history_url_provider.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <algorithm> | |
10 | |
11 #include "base/macros.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/message_loop/message_loop.h" | |
14 #include "base/prefs/pref_service.h" | |
15 #include "base/strings/string_util.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "base/time/time.h" | |
18 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h" | |
19 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h" | |
20 #include "chrome/browser/history/history_service_factory.h" | |
21 #include "chrome/browser/search_engines/chrome_template_url_service_client.h" | |
22 #include "chrome/browser/search_engines/template_url_service_factory.h" | |
23 #include "chrome/common/pref_names.h" | |
24 #include "chrome/test/base/testing_browser_process.h" | |
25 #include "chrome/test/base/testing_profile.h" | |
26 #include "components/history/core/browser/history_service.h" | |
27 #include "components/history/core/browser/url_database.h" | |
28 #include "components/metrics/proto/omnibox_event.pb.h" | |
29 #include "components/metrics/proto/omnibox_input_type.pb.h" | |
30 #include "components/omnibox/browser/autocomplete_match.h" | |
31 #include "components/omnibox/browser/autocomplete_provider.h" | |
32 #include "components/omnibox/browser/autocomplete_provider_listener.h" | |
33 #include "components/omnibox/browser/autocomplete_result.h" | |
34 #include "components/omnibox/browser/history_quick_provider.h" | |
35 #include "components/search_engines/default_search_manager.h" | |
36 #include "components/search_engines/search_terms_data.h" | |
37 #include "components/search_engines/template_url.h" | |
38 #include "components/search_engines/template_url_service.h" | |
39 #include "components/url_formatter/url_fixer.h" | |
40 #include "content/public/test/test_browser_thread_bundle.h" | |
41 #include "testing/gtest/include/gtest/gtest.h" | |
42 | |
43 using base::ASCIIToUTF16; | |
44 using base::Time; | |
45 using base::TimeDelta; | |
46 | |
47 using content::TestBrowserThreadBundle; | |
48 | |
49 struct TestURLInfo { | |
50 const char* url; | |
51 const char* title; | |
52 int visit_count; | |
53 int typed_count; | |
54 int age_in_days; | |
55 } test_db[] = { | |
56 {"http://www.google.com/", "Google", 3, 3, 80}, | |
57 | |
58 // High-quality pages should get a host synthesized as a lower-quality match. | |
59 {"http://slashdot.org/favorite_page.html", "Favorite page", 200, 100, 80}, | |
60 | |
61 // Less popular pages should have hosts synthesized as higher-quality | |
62 // matches. | |
63 {"http://kerneltrap.org/not_very_popular.html", "Less popular", 4, 0, 80}, | |
64 | |
65 // Unpopular pages should not appear in the results at all. | |
66 {"http://freshmeat.net/unpopular.html", "Unpopular", 1, 0, 80}, | |
67 | |
68 // If a host has a match, we should pick it up during host synthesis. | |
69 {"http://news.google.com/?ned=us&topic=n", "Google News - U.S.", 2, 2, 80}, | |
70 {"http://news.google.com/", "Google News", 1, 1, 80}, | |
71 | |
72 // Matches that are normally not inline-autocompletable should be | |
73 // autocompleted if they are shorter substitutes for longer matches that would | |
74 // have been inline autocompleted. | |
75 {"http://synthesisatest.com/foo/", "Test A", 1, 1, 80}, | |
76 {"http://synthesisbtest.com/foo/", "Test B", 1, 1, 80}, | |
77 {"http://synthesisbtest.com/foo/bar.html", "Test B Bar", 2, 2, 80}, | |
78 | |
79 // Suggested short URLs must be "good enough" and must match user input. | |
80 {"http://foo.com/", "Dir", 5, 5, 80}, | |
81 {"http://foo.com/dir/", "Dir", 2, 2, 80}, | |
82 {"http://foo.com/dir/another/", "Dir", 5, 1, 80}, | |
83 {"http://foo.com/dir/another/again/", "Dir", 10, 0, 80}, | |
84 {"http://foo.com/dir/another/again/myfile.html", "File", 10, 2, 80}, | |
85 | |
86 // We throw in a lot of extra URLs here to make sure we're testing the | |
87 // history database's query, not just the autocomplete provider. | |
88 {"http://startest.com/y/a", "A", 2, 2, 80}, | |
89 {"http://startest.com/y/b", "B", 5, 2, 80}, | |
90 {"http://startest.com/x/c", "C", 5, 2, 80}, | |
91 {"http://startest.com/x/d", "D", 5, 5, 80}, | |
92 {"http://startest.com/y/e", "E", 4, 2, 80}, | |
93 {"http://startest.com/y/f", "F", 3, 2, 80}, | |
94 {"http://startest.com/y/g", "G", 3, 2, 80}, | |
95 {"http://startest.com/y/h", "H", 3, 2, 80}, | |
96 {"http://startest.com/y/i", "I", 3, 2, 80}, | |
97 {"http://startest.com/y/j", "J", 3, 2, 80}, | |
98 {"http://startest.com/y/k", "K", 3, 2, 80}, | |
99 {"http://startest.com/y/l", "L", 3, 2, 80}, | |
100 {"http://startest.com/y/m", "M", 3, 2, 80}, | |
101 | |
102 // A file: URL is useful for testing that fixup does the right thing w.r.t. | |
103 // the number of trailing slashes on the user's input. | |
104 {"file:///C:/foo.txt", "", 2, 2, 80}, | |
105 | |
106 // Results with absurdly high typed_counts so that very generic queries like | |
107 // "http" will give consistent results even if more data is added above. | |
108 {"http://bogussite.com/a", "Bogus A", 10002, 10000, 80}, | |
109 {"http://bogussite.com/b", "Bogus B", 10001, 10000, 80}, | |
110 {"http://bogussite.com/c", "Bogus C", 10000, 10000, 80}, | |
111 | |
112 // Domain name with number. | |
113 {"http://www.17173.com/", "Domain with number", 3, 3, 80}, | |
114 | |
115 // URLs to test exact-matching behavior. | |
116 {"http://go/", "Intranet URL", 1, 1, 80}, | |
117 {"http://gooey/", "Intranet URL 2", 5, 5, 80}, | |
118 | |
119 // URLs for testing offset adjustment. | |
120 {"http://www.\xEA\xB5\x90\xEC\x9C\xA1.kr/", "Korean", 2, 2, 80}, | |
121 {"http://spaces.com/path%20with%20spaces/foo.html", "Spaces", 2, 2, 80}, | |
122 {"http://ms/c++%20style%20guide", "Style guide", 2, 2, 80}, | |
123 | |
124 // URLs for testing ctrl-enter behavior. | |
125 {"http://binky/", "Intranet binky", 2, 2, 80}, | |
126 {"http://winky/", "Intranet winky", 2, 2, 80}, | |
127 {"http://www.winky.com/", "Internet winky", 5, 0, 80}, | |
128 | |
129 // URLs used by EmptyVisits. | |
130 {"http://pandora.com/", "Pandora", 2, 2, 80}, | |
131 // This entry is explicitly added more recently than | |
132 // history::kLowQualityMatchAgeLimitInDays. | |
133 // {"http://pa/", "pa", 0, 0, 80}, | |
134 | |
135 // For intranet based tests. | |
136 {"http://intra/one", "Intranet", 2, 2, 80}, | |
137 {"http://intra/two", "Intranet two", 1, 1, 80}, | |
138 {"http://intra/three", "Intranet three", 2, 2, 80}, | |
139 {"http://moo/bar", "Intranet moo", 1, 1, 80}, | |
140 {"http://typedhost/typedpath", "Intranet typed", 1, 1, 80}, | |
141 {"http://typedhost/untypedpath", "Intranet untyped", 1, 0, 80}, | |
142 | |
143 {"http://x.com/one", "Internet", 2, 2, 80}, | |
144 {"http://x.com/two", "Internet two", 1, 1, 80}, | |
145 {"http://x.com/three", "Internet three", 2, 2, 80}, | |
146 | |
147 // For punycode tests. | |
148 {"http://puny.xn--1lq90ic7f1rc.cn/", "Punycode", 2, 2, 5 }, | |
149 | |
150 // For experimental HUP scoring test. | |
151 {"http://7.com/1a", "One", 8, 4, 4}, | |
152 {"http://7.com/2a", "Two A", 4, 2, 8}, | |
153 {"http://7.com/2b", "Two B", 4, 1, 8}, | |
154 {"http://7.com/3a", "Three", 2, 1, 16}, | |
155 {"http://7.com/4a", "Four A", 1, 1, 32}, | |
156 {"http://7.com/4b", "Four B", 1, 1, 64}, | |
157 {"http://7.com/5a", "Five A", 8, 0, 64}, // never typed. | |
158 }; | |
159 | |
160 class HistoryURLProviderTest : public testing::Test, | |
161 public AutocompleteProviderListener { | |
162 public: | |
163 struct UrlAndLegalDefault { | |
164 std::string url; | |
165 bool allowed_to_be_default_match; | |
166 }; | |
167 | |
168 HistoryURLProviderTest() | |
169 : sort_matches_(false) { | |
170 HistoryQuickProvider::set_disabled(true); | |
171 } | |
172 | |
173 ~HistoryURLProviderTest() override { | |
174 HistoryQuickProvider::set_disabled(false); | |
175 } | |
176 | |
177 // AutocompleteProviderListener: | |
178 void OnProviderUpdate(bool updated_matches) override; | |
179 | |
180 protected: | |
181 static scoped_ptr<KeyedService> CreateTemplateURLService( | |
182 content::BrowserContext* context) { | |
183 Profile* profile = static_cast<Profile*>(context); | |
184 return make_scoped_ptr(new TemplateURLService( | |
185 profile->GetPrefs(), make_scoped_ptr(new SearchTermsData), NULL, | |
186 scoped_ptr<TemplateURLServiceClient>(new ChromeTemplateURLServiceClient( | |
187 HistoryServiceFactory::GetForProfile( | |
188 profile, ServiceAccessType::EXPLICIT_ACCESS))), | |
189 NULL, NULL, base::Closure())); | |
190 } | |
191 | |
192 // testing::Test | |
193 void SetUp() override { | |
194 ASSERT_TRUE(SetUpImpl(false)); | |
195 } | |
196 void TearDown() override; | |
197 | |
198 // Does the real setup. | |
199 bool SetUpImpl(bool no_db) WARN_UNUSED_RESULT; | |
200 | |
201 // Fills test data into the history system. | |
202 void FillData(); | |
203 | |
204 // Runs an autocomplete query on |text| and checks to see that the returned | |
205 // results' destination URLs match those provided. Also allows checking | |
206 // that the input type was identified correctly. | |
207 void RunTest(const base::string16& text, | |
208 const std::string& desired_tld, | |
209 bool prevent_inline_autocomplete, | |
210 const UrlAndLegalDefault* expected_urls, | |
211 size_t num_results, | |
212 metrics::OmniboxInputType::Type* identified_input_type); | |
213 | |
214 // A version of the above without the final |type| output parameter. | |
215 void RunTest(const base::string16& text, | |
216 const std::string& desired_tld, | |
217 bool prevent_inline_autocomplete, | |
218 const UrlAndLegalDefault* expected_urls, | |
219 size_t num_results) { | |
220 metrics::OmniboxInputType::Type type; | |
221 return RunTest(text, desired_tld, prevent_inline_autocomplete, | |
222 expected_urls, num_results, &type); | |
223 } | |
224 | |
225 content::TestBrowserThreadBundle thread_bundle_; | |
226 ACMatches matches_; | |
227 scoped_ptr<TestingProfile> profile_; | |
228 scoped_ptr<ChromeAutocompleteProviderClient> client_; | |
229 history::HistoryService* history_service_; | |
230 scoped_refptr<HistoryURLProvider> autocomplete_; | |
231 // Should the matches be sorted and duplicates removed? | |
232 bool sort_matches_; | |
233 }; | |
234 | |
235 class HistoryURLProviderTestNoDB : public HistoryURLProviderTest { | |
236 protected: | |
237 void SetUp() override { | |
238 ASSERT_TRUE(SetUpImpl(true)); | |
239 } | |
240 }; | |
241 | |
242 class HistoryURLProviderTestNoSearchProvider : public HistoryURLProviderTest { | |
243 protected: | |
244 void SetUp() override { | |
245 DefaultSearchManager::SetFallbackSearchEnginesDisabledForTesting(true); | |
246 HistoryURLProviderTest::SetUp(); | |
247 } | |
248 | |
249 void TearDown() override { | |
250 HistoryURLProviderTest::TearDown(); | |
251 DefaultSearchManager::SetFallbackSearchEnginesDisabledForTesting(false); | |
252 } | |
253 }; | |
254 | |
255 void HistoryURLProviderTest::OnProviderUpdate(bool updated_matches) { | |
256 if (autocomplete_->done()) | |
257 base::MessageLoop::current()->QuitWhenIdle(); | |
258 } | |
259 | |
260 bool HistoryURLProviderTest::SetUpImpl(bool no_db) { | |
261 profile_.reset(new TestingProfile()); | |
262 client_.reset(new ChromeAutocompleteProviderClient(profile_.get())); | |
263 if (!(profile_->CreateHistoryService(true, no_db))) | |
264 return false; | |
265 if (!no_db) { | |
266 profile_->BlockUntilHistoryProcessesPendingRequests(); | |
267 profile_->BlockUntilHistoryIndexIsRefreshed(); | |
268 } | |
269 profile_->GetPrefs()->SetString(prefs::kAcceptLanguages, "en-US,en,ko"); | |
270 history_service_ = HistoryServiceFactory::GetForProfile( | |
271 profile_.get(), ServiceAccessType::EXPLICIT_ACCESS); | |
272 | |
273 autocomplete_ = new HistoryURLProvider(client_.get(), this); | |
274 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( | |
275 profile_.get(), &HistoryURLProviderTest::CreateTemplateURLService); | |
276 FillData(); | |
277 return true; | |
278 } | |
279 | |
280 void HistoryURLProviderTest::TearDown() { | |
281 autocomplete_ = NULL; | |
282 } | |
283 | |
284 void HistoryURLProviderTest::FillData() { | |
285 // Most visits are a long time ago (some tests require this since we do some | |
286 // special logic for things visited very recently). Note that this time must | |
287 // be more recent than the "expire history" threshold for the data to be kept | |
288 // in the main database. | |
289 // | |
290 // TODO(brettw) It would be nice if we could test this behavior, in which | |
291 // case the time would be specifed in the test_db structure. | |
292 const Time now = Time::Now(); | |
293 | |
294 for (size_t i = 0; i < arraysize(test_db); ++i) { | |
295 const TestURLInfo& cur = test_db[i]; | |
296 const GURL current_url(cur.url); | |
297 history_service_->AddPageWithDetails( | |
298 current_url, base::UTF8ToUTF16(cur.title), cur.visit_count, | |
299 cur.typed_count, now - TimeDelta::FromDays(cur.age_in_days), false, | |
300 history::SOURCE_BROWSED); | |
301 } | |
302 | |
303 history_service_->AddPageWithDetails( | |
304 GURL("http://pa/"), base::UTF8ToUTF16("pa"), 0, 0, | |
305 Time::Now() - | |
306 TimeDelta::FromDays(history::kLowQualityMatchAgeLimitInDays - 1), | |
307 false, history::SOURCE_BROWSED); | |
308 } | |
309 | |
310 void HistoryURLProviderTest::RunTest( | |
311 const base::string16& text, | |
312 const std::string& desired_tld, | |
313 bool prevent_inline_autocomplete, | |
314 const UrlAndLegalDefault* expected_urls, | |
315 size_t num_results, | |
316 metrics::OmniboxInputType::Type* identified_input_type) { | |
317 AutocompleteInput input(text, base::string16::npos, desired_tld, GURL(), | |
318 metrics::OmniboxEventProto::INVALID_SPEC, | |
319 prevent_inline_autocomplete, false, true, true, false, | |
320 ChromeAutocompleteSchemeClassifier(profile_.get())); | |
321 *identified_input_type = input.type(); | |
322 autocomplete_->Start(input, false); | |
323 if (!autocomplete_->done()) | |
324 base::MessageLoop::current()->Run(); | |
325 | |
326 matches_ = autocomplete_->matches(); | |
327 if (sort_matches_) { | |
328 TemplateURLService* service = | |
329 TemplateURLServiceFactory::GetForProfile(profile_.get()); | |
330 for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) { | |
331 i->ComputeStrippedDestinationURL( | |
332 input, client_->GetAcceptLanguages(), service); | |
333 } | |
334 AutocompleteResult::DedupMatchesByDestination( | |
335 input.current_page_classification(), false, &matches_); | |
336 std::sort(matches_.begin(), matches_.end(), | |
337 &AutocompleteMatch::MoreRelevant); | |
338 } | |
339 ASSERT_EQ(num_results, matches_.size()) << "Input text: " << text | |
340 << "\nTLD: \"" << desired_tld << "\""; | |
341 for (size_t i = 0; i < num_results; ++i) { | |
342 EXPECT_EQ(expected_urls[i].url, matches_[i].destination_url.spec()); | |
343 EXPECT_EQ(expected_urls[i].allowed_to_be_default_match, | |
344 matches_[i].allowed_to_be_default_match); | |
345 } | |
346 } | |
347 | |
348 TEST_F(HistoryURLProviderTest, PromoteShorterURLs) { | |
349 // Test that hosts get synthesized below popular pages. | |
350 const UrlAndLegalDefault expected_nonsynth[] = { | |
351 { "http://slashdot.org/favorite_page.html", false }, | |
352 { "http://slashdot.org/", false } | |
353 }; | |
354 RunTest(ASCIIToUTF16("slash"), std::string(), true, expected_nonsynth, | |
355 arraysize(expected_nonsynth)); | |
356 | |
357 // Test that hosts get synthesized above less popular pages. | |
358 const UrlAndLegalDefault expected_synth[] = { | |
359 { "http://kerneltrap.org/", false }, | |
360 { "http://kerneltrap.org/not_very_popular.html", false } | |
361 }; | |
362 RunTest(ASCIIToUTF16("kernel"), std::string(), true, expected_synth, | |
363 arraysize(expected_synth)); | |
364 | |
365 // Test that unpopular pages are ignored completely. | |
366 RunTest(ASCIIToUTF16("fresh"), std::string(), true, NULL, 0); | |
367 | |
368 // Test that if we create or promote shorter suggestions that would not | |
369 // normally be inline autocompletable, we make them inline autocompletable if | |
370 // the original suggestion (that we replaced as "top") was inline | |
371 // autocompletable. | |
372 const UrlAndLegalDefault expected_synthesisa[] = { | |
373 { "http://synthesisatest.com/", true }, | |
374 { "http://synthesisatest.com/foo/", true } | |
375 }; | |
376 RunTest(ASCIIToUTF16("synthesisa"), std::string(), false, expected_synthesisa, | |
377 arraysize(expected_synthesisa)); | |
378 EXPECT_LT(matches_.front().relevance, 1200); | |
379 const UrlAndLegalDefault expected_synthesisb[] = { | |
380 { "http://synthesisbtest.com/foo/", true }, | |
381 { "http://synthesisbtest.com/foo/bar.html", true } | |
382 }; | |
383 RunTest(ASCIIToUTF16("synthesisb"), std::string(), false, expected_synthesisb, | |
384 arraysize(expected_synthesisb)); | |
385 EXPECT_GE(matches_.front().relevance, 1410); | |
386 | |
387 // Test that if we have a synthesized host that matches a suggestion, they | |
388 // get combined into one. | |
389 const UrlAndLegalDefault expected_combine[] = { | |
390 { "http://news.google.com/", false }, | |
391 { "http://news.google.com/?ned=us&topic=n", false }, | |
392 }; | |
393 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("news"), std::string(), true, | |
394 expected_combine, | |
395 arraysize(expected_combine))); | |
396 // The title should also have gotten set properly on the host for the | |
397 // synthesized one, since it was also in the results. | |
398 EXPECT_EQ(ASCIIToUTF16("Google News"), matches_.front().description); | |
399 | |
400 // Test that short URL matching works correctly as the user types more | |
401 // (several tests): | |
402 // The entry for foo.com is the best of all five foo.com* entries. | |
403 const UrlAndLegalDefault short_1[] = { | |
404 { "http://foo.com/", false }, | |
405 { "http://foo.com/dir/another/again/myfile.html", false }, | |
406 { "http://foo.com/dir/", false } | |
407 }; | |
408 RunTest(ASCIIToUTF16("foo"), std::string(), true, short_1, | |
409 arraysize(short_1)); | |
410 | |
411 // When the user types the whole host, make sure we don't get two results for | |
412 // it. | |
413 const UrlAndLegalDefault short_2[] = { | |
414 { "http://foo.com/", true }, | |
415 { "http://foo.com/dir/another/again/myfile.html", false }, | |
416 { "http://foo.com/dir/", false }, | |
417 { "http://foo.com/dir/another/", false } | |
418 }; | |
419 RunTest(ASCIIToUTF16("foo.com"), std::string(), true, short_2, | |
420 arraysize(short_2)); | |
421 RunTest(ASCIIToUTF16("foo.com/"), std::string(), true, short_2, | |
422 arraysize(short_2)); | |
423 | |
424 // The filename is the second best of the foo.com* entries, but there is a | |
425 // shorter URL that's "good enough". The host doesn't match the user input | |
426 // and so should not appear. | |
427 const UrlAndLegalDefault short_3[] = { | |
428 { "http://foo.com/dir/another/", false }, | |
429 { "http://foo.com/d", true }, | |
430 { "http://foo.com/dir/another/again/myfile.html", false }, | |
431 { "http://foo.com/dir/", false } | |
432 }; | |
433 RunTest(ASCIIToUTF16("foo.com/d"), std::string(), true, short_3, | |
434 arraysize(short_3)); | |
435 // If prevent_inline_autocomplete is false, we won't bother creating the | |
436 // URL-what-you-typed match because we have promoted inline autocompletions. | |
437 const UrlAndLegalDefault short_3_allow_inline[] = { | |
438 { "http://foo.com/dir/another/", true }, | |
439 { "http://foo.com/dir/another/again/myfile.html", true }, | |
440 { "http://foo.com/dir/", true } | |
441 }; | |
442 RunTest(ASCIIToUTF16("foo.com/d"), std::string(), false, short_3_allow_inline, | |
443 arraysize(short_3_allow_inline)); | |
444 | |
445 // We shouldn't promote shorter URLs than the best if they're not good | |
446 // enough. | |
447 const UrlAndLegalDefault short_4[] = { | |
448 { "http://foo.com/dir/another/again/myfile.html", false }, | |
449 { "http://foo.com/dir/another/a", true }, | |
450 { "http://foo.com/dir/another/again/", false } | |
451 }; | |
452 RunTest(ASCIIToUTF16("foo.com/dir/another/a"), std::string(), true, short_4, | |
453 arraysize(short_4)); | |
454 // If prevent_inline_autocomplete is false, we won't bother creating the | |
455 // URL-what-you-typed match because we have promoted inline autocompletions. | |
456 const UrlAndLegalDefault short_4_allow_inline[] = { | |
457 { "http://foo.com/dir/another/again/myfile.html", true }, | |
458 { "http://foo.com/dir/another/again/", true } | |
459 }; | |
460 RunTest(ASCIIToUTF16("foo.com/dir/another/a"), std::string(), false, | |
461 short_4_allow_inline, arraysize(short_4_allow_inline)); | |
462 | |
463 // Exact matches should always be best no matter how much more another match | |
464 // has been typed. | |
465 const UrlAndLegalDefault short_5a[] = { | |
466 { "http://gooey/", true }, | |
467 { "http://www.google.com/", true }, | |
468 { "http://go/", true } | |
469 }; | |
470 const UrlAndLegalDefault short_5b[] = { | |
471 { "http://go/", true }, | |
472 { "http://gooey/", true }, | |
473 { "http://www.google.com/", true } | |
474 }; | |
475 RunTest(ASCIIToUTF16("g"), std::string(), false, short_5a, | |
476 arraysize(short_5a)); | |
477 RunTest(ASCIIToUTF16("go"), std::string(), false, short_5b, | |
478 arraysize(short_5b)); | |
479 } | |
480 | |
481 TEST_F(HistoryURLProviderTest, CullRedirects) { | |
482 // URLs we will be using, plus the visit counts they will initially get | |
483 // (the redirect set below will also increment the visit counts). We want | |
484 // the results to be in A,B,C order. Note also that our visit counts are | |
485 // all high enough so that domain synthesizing won't get triggered. | |
486 struct TestCase { | |
487 const char* url; | |
488 int count; | |
489 } test_cases[] = { | |
490 {"http://redirects/A", 30}, | |
491 {"http://redirects/B", 20}, | |
492 {"http://redirects/C", 10} | |
493 }; | |
494 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
495 history_service_->AddPageWithDetails(GURL(test_cases[i].url), | |
496 ASCIIToUTF16("Title"), test_cases[i].count, test_cases[i].count, | |
497 Time::Now(), false, history::SOURCE_BROWSED); | |
498 } | |
499 | |
500 // Create a B->C->A redirect chain, but set the visit counts such that they | |
501 // will appear in A,B,C order in the results. The autocomplete query will | |
502 // search for the most recent visit when looking for redirects, so this will | |
503 // be found even though the previous visits had no redirects. | |
504 history::RedirectList redirects_to_a; | |
505 redirects_to_a.push_back(GURL(test_cases[1].url)); | |
506 redirects_to_a.push_back(GURL(test_cases[2].url)); | |
507 redirects_to_a.push_back(GURL(test_cases[0].url)); | |
508 history_service_->AddPage(GURL(test_cases[0].url), base::Time::Now(), | |
509 NULL, 0, GURL(), redirects_to_a, ui::PAGE_TRANSITION_TYPED, | |
510 history::SOURCE_BROWSED, true); | |
511 | |
512 // Because all the results are part of a redirect chain with other results, | |
513 // all but the first one (A) should be culled. We should get the default | |
514 // "what you typed" result, plus this one. | |
515 const base::string16 typing(ASCIIToUTF16("http://redirects/")); | |
516 const UrlAndLegalDefault expected_results[] = { | |
517 { test_cases[0].url, false }, | |
518 { base::UTF16ToUTF8(typing), true } | |
519 }; | |
520 RunTest(typing, std::string(), true, expected_results, | |
521 arraysize(expected_results)); | |
522 | |
523 // If prevent_inline_autocomplete is false, we won't bother creating the | |
524 // URL-what-you-typed match because we have promoted inline autocompletions. | |
525 // The result set should instead consist of a single URL representing the | |
526 // whole set of redirects. | |
527 const UrlAndLegalDefault expected_results_allow_inlining[] = { | |
528 { test_cases[0].url, true } | |
529 }; | |
530 RunTest(typing, std::string(), false, expected_results_allow_inlining, | |
531 arraysize(expected_results_allow_inlining)); | |
532 } | |
533 | |
534 TEST_F(HistoryURLProviderTestNoSearchProvider, WhatYouTypedNoSearchProvider) { | |
535 // When no search provider is available, make sure we provide WYT matches | |
536 // for text that could be a URL. | |
537 | |
538 const UrlAndLegalDefault results_1[] = { | |
539 { "http://wytmatch/", true } | |
540 }; | |
541 RunTest(ASCIIToUTF16("wytmatch"), std::string(), false, results_1, | |
542 arraysize(results_1)); | |
543 | |
544 RunTest(ASCIIToUTF16("wytmatch foo bar"), std::string(), false, NULL, 0); | |
545 RunTest(ASCIIToUTF16("wytmatch+foo+bar"), std::string(), false, NULL, 0); | |
546 | |
547 const UrlAndLegalDefault results_2[] = { | |
548 { "http://wytmatch+foo+bar.com/", true } | |
549 }; | |
550 RunTest(ASCIIToUTF16("wytmatch+foo+bar.com"), std::string(), false, | |
551 results_2, arraysize(results_2)); | |
552 } | |
553 | |
554 TEST_F(HistoryURLProviderTest, WhatYouTyped) { | |
555 // Make sure we suggest a What You Typed match at the right times. | |
556 RunTest(ASCIIToUTF16("wytmatch"), std::string(), false, NULL, 0); | |
557 RunTest(ASCIIToUTF16("wytmatch foo bar"), std::string(), false, NULL, 0); | |
558 RunTest(ASCIIToUTF16("wytmatch+foo+bar"), std::string(), false, NULL, 0); | |
559 RunTest(ASCIIToUTF16("wytmatch+foo+bar.com"), std::string(), false, NULL, 0); | |
560 | |
561 const UrlAndLegalDefault results_1[] = { | |
562 { "http://www.wytmatch.com/", true } | |
563 }; | |
564 RunTest(ASCIIToUTF16("wytmatch"), "com", false, results_1, | |
565 arraysize(results_1)); | |
566 | |
567 const UrlAndLegalDefault results_2[] = { | |
568 { "http://wytmatch%20foo%20bar/", true } | |
569 }; | |
570 RunTest(ASCIIToUTF16("http://wytmatch foo bar"), std::string(), false, | |
571 results_2, arraysize(results_2)); | |
572 | |
573 const UrlAndLegalDefault results_3[] = { | |
574 { "https://wytmatch%20foo%20bar/", true } | |
575 }; | |
576 RunTest(ASCIIToUTF16("https://wytmatch foo bar"), std::string(), false, | |
577 results_3, arraysize(results_3)); | |
578 } | |
579 | |
580 TEST_F(HistoryURLProviderTest, Fixup) { | |
581 // Test for various past crashes we've had. | |
582 RunTest(ASCIIToUTF16("\\"), std::string(), false, NULL, 0); | |
583 RunTest(ASCIIToUTF16("#"), std::string(), false, NULL, 0); | |
584 RunTest(ASCIIToUTF16("%20"), std::string(), false, NULL, 0); | |
585 const UrlAndLegalDefault fixup_crash[] = { | |
586 { "http://%EF%BD%A5@s/", true } | |
587 }; | |
588 RunTest(base::WideToUTF16(L"\uff65@s"), std::string(), false, fixup_crash, | |
589 arraysize(fixup_crash)); | |
590 RunTest(base::WideToUTF16(L"\u2015\u2015@ \uff7c"), std::string(), false, | |
591 NULL, 0); | |
592 | |
593 // Fixing up "file:" should result in an inline autocomplete offset of just | |
594 // after "file:", not just after "file://". | |
595 const base::string16 input_1(ASCIIToUTF16("file:")); | |
596 const UrlAndLegalDefault fixup_1[] = { | |
597 { "file:///C:/foo.txt", true } | |
598 }; | |
599 ASSERT_NO_FATAL_FAILURE(RunTest(input_1, std::string(), false, fixup_1, | |
600 arraysize(fixup_1))); | |
601 EXPECT_EQ(ASCIIToUTF16("///C:/foo.txt"), | |
602 matches_.front().inline_autocompletion); | |
603 | |
604 // Fixing up "http:/" should result in an inline autocomplete offset of just | |
605 // after "http:/", not just after "http:". | |
606 const base::string16 input_2(ASCIIToUTF16("http:/")); | |
607 const UrlAndLegalDefault fixup_2[] = { | |
608 { "http://bogussite.com/a", true }, | |
609 { "http://bogussite.com/b", true }, | |
610 { "http://bogussite.com/c", true } | |
611 }; | |
612 ASSERT_NO_FATAL_FAILURE(RunTest(input_2, std::string(), false, fixup_2, | |
613 arraysize(fixup_2))); | |
614 EXPECT_EQ(ASCIIToUTF16("/bogussite.com/a"), | |
615 matches_.front().inline_autocompletion); | |
616 | |
617 // Adding a TLD to a small number like "56" should result in "www.56.com" | |
618 // rather than "0.0.0.56.com". | |
619 const UrlAndLegalDefault fixup_3[] = { | |
620 { "http://www.56.com/", true } | |
621 }; | |
622 RunTest(ASCIIToUTF16("56"), "com", true, fixup_3, arraysize(fixup_3)); | |
623 | |
624 // An input looks like a IP address like "127.0.0.1" should result in | |
625 // "http://127.0.0.1/". | |
626 const UrlAndLegalDefault fixup_4[] = { | |
627 { "http://127.0.0.1/", true } | |
628 }; | |
629 RunTest(ASCIIToUTF16("127.0.0.1"), std::string(), false, fixup_4, | |
630 arraysize(fixup_4)); | |
631 | |
632 // An number "17173" should result in "http://www.17173.com/" in db. | |
633 const UrlAndLegalDefault fixup_5[] = { | |
634 { "http://www.17173.com/", true } | |
635 }; | |
636 RunTest(ASCIIToUTF16("17173"), std::string(), false, fixup_5, | |
637 arraysize(fixup_5)); | |
638 } | |
639 | |
640 // Make sure the results for the input 'p' don't change between the first and | |
641 // second passes. | |
642 TEST_F(HistoryURLProviderTest, EmptyVisits) { | |
643 // Wait for history to create the in memory DB. | |
644 profile_->BlockUntilHistoryProcessesPendingRequests(); | |
645 | |
646 AutocompleteInput input( | |
647 ASCIIToUTF16("pa"), base::string16::npos, std::string(), GURL(), | |
648 metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true, false, | |
649 ChromeAutocompleteSchemeClassifier(profile_.get())); | |
650 autocomplete_->Start(input, false); | |
651 // HistoryURLProvider shouldn't be done (waiting on async results). | |
652 EXPECT_FALSE(autocomplete_->done()); | |
653 | |
654 // We should get back an entry for pandora. | |
655 matches_ = autocomplete_->matches(); | |
656 ASSERT_GT(matches_.size(), 0u); | |
657 EXPECT_EQ(GURL("http://pandora.com/"), matches_[0].destination_url); | |
658 int pandora_relevance = matches_[0].relevance; | |
659 | |
660 // Run the message loop. When |autocomplete_| finishes the loop is quit. | |
661 base::MessageLoop::current()->Run(); | |
662 EXPECT_TRUE(autocomplete_->done()); | |
663 matches_ = autocomplete_->matches(); | |
664 ASSERT_GT(matches_.size(), 0u); | |
665 EXPECT_EQ(GURL("http://pandora.com/"), matches_[0].destination_url); | |
666 EXPECT_EQ(pandora_relevance, matches_[0].relevance); | |
667 } | |
668 | |
669 TEST_F(HistoryURLProviderTestNoDB, NavigateWithoutDB) { | |
670 // Ensure that we will still produce matches for navigation when there is no | |
671 // database. | |
672 UrlAndLegalDefault navigation_1[] = { | |
673 { "http://test.com/", true } | |
674 }; | |
675 RunTest(ASCIIToUTF16("test.com"), std::string(), false, navigation_1, | |
676 arraysize(navigation_1)); | |
677 | |
678 UrlAndLegalDefault navigation_2[] = { | |
679 { "http://slash/", true } | |
680 }; | |
681 RunTest(ASCIIToUTF16("slash"), std::string(), false, navigation_2, | |
682 arraysize(navigation_2)); | |
683 | |
684 RunTest(ASCIIToUTF16("this is a query"), std::string(), false, NULL, 0); | |
685 } | |
686 | |
687 TEST_F(HistoryURLProviderTest, DontAutocompleteOnTrailingWhitespace) { | |
688 AutocompleteInput input( | |
689 ASCIIToUTF16("slash "), base::string16::npos, std::string(), GURL(), | |
690 metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true, false, | |
691 ChromeAutocompleteSchemeClassifier(profile_.get())); | |
692 autocomplete_->Start(input, false); | |
693 if (!autocomplete_->done()) | |
694 base::MessageLoop::current()->Run(); | |
695 | |
696 // None of the matches should attempt to autocomplete. | |
697 matches_ = autocomplete_->matches(); | |
698 for (size_t i = 0; i < matches_.size(); ++i) { | |
699 EXPECT_TRUE(matches_[i].inline_autocompletion.empty()); | |
700 EXPECT_FALSE(matches_[i].allowed_to_be_default_match); | |
701 } | |
702 } | |
703 | |
704 TEST_F(HistoryURLProviderTest, TreatEmailsAsSearches) { | |
705 // Visiting foo.com should not make this string be treated as a navigation. | |
706 // That means the result should be scored around 1200 ("what you typed") | |
707 // and not 1400+. | |
708 const UrlAndLegalDefault expected[] = { | |
709 { "http://user@foo.com/", true } | |
710 }; | |
711 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("user@foo.com"), std::string(), | |
712 false, expected, arraysize(expected))); | |
713 EXPECT_LE(1200, matches_[0].relevance); | |
714 EXPECT_LT(matches_[0].relevance, 1210); | |
715 } | |
716 | |
717 TEST_F(HistoryURLProviderTest, IntranetURLsWithPaths) { | |
718 struct TestCase { | |
719 const char* input; | |
720 int relevance; | |
721 } test_cases[] = { | |
722 { "fooey", 0 }, | |
723 { "fooey/", 1200 }, // 1200 for URL would still navigate by default. | |
724 { "fooey/a", 1200 }, // 1200 for UNKNOWN would not. | |
725 { "fooey/a b", 1200 }, // Also UNKNOWN. | |
726 { "gooey", 1410 }, | |
727 { "gooey/", 1410 }, | |
728 { "gooey/a", 1400 }, | |
729 { "gooey/a b", 1400 }, | |
730 }; | |
731 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
732 SCOPED_TRACE(test_cases[i].input); | |
733 if (test_cases[i].relevance == 0) { | |
734 RunTest(ASCIIToUTF16(test_cases[i].input), std::string(), false, NULL, 0); | |
735 } else { | |
736 const UrlAndLegalDefault output[] = { | |
737 {url_formatter::FixupURL(test_cases[i].input, std::string()).spec(), | |
738 true}}; | |
739 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16(test_cases[i].input), | |
740 std::string(), false, output, arraysize(output))); | |
741 // Actual relevance should be at least what test_cases expects and | |
742 // and no more than 10 more. | |
743 EXPECT_LE(test_cases[i].relevance, matches_[0].relevance); | |
744 EXPECT_LT(matches_[0].relevance, test_cases[i].relevance + 10); | |
745 } | |
746 } | |
747 } | |
748 | |
749 // Makes sure autocompletion happens for intranet sites that have been | |
750 // previoulsy visited. | |
751 TEST_F(HistoryURLProviderTest, IntranetURLCompletion) { | |
752 sort_matches_ = true; | |
753 | |
754 const UrlAndLegalDefault expected1[] = { | |
755 { "http://intra/three", true }, | |
756 { "http://intra/two", true } | |
757 }; | |
758 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("intra/t"), std::string(), false, | |
759 expected1, arraysize(expected1))); | |
760 EXPECT_LE(1410, matches_[0].relevance); | |
761 EXPECT_LT(matches_[0].relevance, 1420); | |
762 // It uses the default scoring. | |
763 EXPECT_EQ(matches_[1].relevance, 1203); | |
764 | |
765 const UrlAndLegalDefault expected2[] = { | |
766 { "http://moo/b", true }, | |
767 { "http://moo/bar", true } | |
768 }; | |
769 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("moo/b"), std::string(), false, | |
770 expected2, arraysize(expected2))); | |
771 // The url what you typed match should be around 1400, otherwise the | |
772 // search what you typed match is going to be first. | |
773 EXPECT_LE(1400, matches_[0].relevance); | |
774 EXPECT_LT(matches_[0].relevance, 1410); | |
775 | |
776 const UrlAndLegalDefault expected3[] = { | |
777 { "http://intra/one", true }, | |
778 { "http://intra/three", true }, | |
779 { "http://intra/two", true } | |
780 }; | |
781 RunTest(ASCIIToUTF16("intra"), std::string(), false, expected3, | |
782 arraysize(expected3)); | |
783 | |
784 const UrlAndLegalDefault expected4[] = { | |
785 { "http://intra/one", true }, | |
786 { "http://intra/three", true }, | |
787 { "http://intra/two", true } | |
788 }; | |
789 RunTest(ASCIIToUTF16("intra/"), std::string(), false, expected4, | |
790 arraysize(expected4)); | |
791 | |
792 const UrlAndLegalDefault expected5[] = { | |
793 { "http://intra/one", true } | |
794 }; | |
795 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("intra/o"), std::string(), false, | |
796 expected5, arraysize(expected5))); | |
797 EXPECT_LE(1410, matches_[0].relevance); | |
798 EXPECT_LT(matches_[0].relevance, 1420); | |
799 | |
800 const UrlAndLegalDefault expected6[] = { | |
801 { "http://intra/x", true } | |
802 }; | |
803 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("intra/x"), std::string(), false, | |
804 expected6, arraysize(expected6))); | |
805 EXPECT_LE(1400, matches_[0].relevance); | |
806 EXPECT_LT(matches_[0].relevance, 1410); | |
807 | |
808 const UrlAndLegalDefault expected7[] = { | |
809 { "http://typedhost/untypedpath", true } | |
810 }; | |
811 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("typedhost/untypedpath"), | |
812 std::string(), false, expected7, | |
813 arraysize(expected7))); | |
814 EXPECT_LE(1400, matches_[0].relevance); | |
815 EXPECT_LT(matches_[0].relevance, 1410); | |
816 } | |
817 | |
818 TEST_F(HistoryURLProviderTest, CrashDueToFixup) { | |
819 // This test passes if we don't crash. The results don't matter. | |
820 const char* const test_cases[] = { | |
821 "//c", | |
822 "\\@st", | |
823 "view-source:x", | |
824 }; | |
825 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
826 AutocompleteInput input( | |
827 ASCIIToUTF16(test_cases[i]), base::string16::npos, std::string(), | |
828 GURL(), metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, | |
829 true, false, ChromeAutocompleteSchemeClassifier(profile_.get())); | |
830 autocomplete_->Start(input, false); | |
831 if (!autocomplete_->done()) | |
832 base::MessageLoop::current()->Run(); | |
833 } | |
834 } | |
835 | |
836 TEST_F(HistoryURLProviderTest, DoesNotProvideMatchesOnFocus) { | |
837 AutocompleteInput input( | |
838 ASCIIToUTF16("foo"), base::string16::npos, std::string(), GURL(), | |
839 metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true, true, | |
840 ChromeAutocompleteSchemeClassifier(profile_.get())); | |
841 autocomplete_->Start(input, false); | |
842 EXPECT_TRUE(autocomplete_->matches().empty()); | |
843 } | |
844 | |
845 TEST_F(HistoryURLProviderTest, DoesNotInlinePunycodeMatches) { | |
846 // A URL that matches due to a match in the punycode URL are allowed to be the | |
847 // default match if the URL doesn't get rendered as international characters | |
848 // in the given locale. | |
849 const UrlAndLegalDefault expected_true[] = { | |
850 { "http://puny.xn--1lq90ic7f1rc.cn/", true }, | |
851 }; | |
852 UrlAndLegalDefault expected_false[] = { | |
853 { "http://puny.xn--1lq90ic7f1rc.cn/", false }, | |
854 }; | |
855 RunTest(ASCIIToUTF16("pun"), std::string(), false, expected_true, | |
856 arraysize(expected_true)); | |
857 RunTest(ASCIIToUTF16("puny."), std::string(), false, expected_true, | |
858 arraysize(expected_true)); | |
859 RunTest(ASCIIToUTF16("puny.x"), std::string(), false, expected_true, | |
860 arraysize(expected_true)); | |
861 RunTest(ASCIIToUTF16("puny.xn"), std::string(), false, expected_true, | |
862 arraysize(expected_true)); | |
863 RunTest(ASCIIToUTF16("puny.xn--"), std::string(), false, expected_true, | |
864 arraysize(expected_true)); | |
865 RunTest(ASCIIToUTF16("puny.xn--1l"), std::string(), false, expected_true, | |
866 arraysize(expected_true)); | |
867 RunTest(ASCIIToUTF16("puny.xn--1lq90ic7f1rc"), std::string(), false, | |
868 expected_true, arraysize(expected_true)); | |
869 RunTest(ASCIIToUTF16("puny.xn--1lq90ic7f1rc."), std::string(), false, | |
870 expected_true, arraysize(expected_true)); | |
871 // Set the language so the punycode part of the URL is rendered as | |
872 // international characters. Then this match should not be allowed to be | |
873 // the default match if the inline autocomplete text starts in the middle | |
874 // of the international characters. | |
875 profile_->GetPrefs()->SetString(prefs::kAcceptLanguages, "zh-CN"); | |
876 RunTest(ASCIIToUTF16("pun"), std::string(), false, expected_true, | |
877 arraysize(expected_true)); | |
878 RunTest(ASCIIToUTF16("puny."), std::string(), false, expected_true, | |
879 arraysize(expected_true)); | |
880 RunTest(ASCIIToUTF16("puny.x"), std::string(), false, expected_false, | |
881 arraysize(expected_false)); | |
882 RunTest(ASCIIToUTF16("puny.xn"), std::string(), false, expected_false, | |
883 arraysize(expected_false)); | |
884 RunTest(ASCIIToUTF16("puny.xn--"), std::string(), false, expected_false, | |
885 arraysize(expected_false)); | |
886 RunTest(ASCIIToUTF16("puny.xn--1l"), std::string(), false, expected_false, | |
887 arraysize(expected_false)); | |
888 RunTest(ASCIIToUTF16("puny.xn--1lq90ic7f1rc"), std::string(), false, | |
889 expected_true, arraysize(expected_true)); | |
890 RunTest(ASCIIToUTF16("puny.xn--1lq90ic7f1rc."), std::string(), false, | |
891 expected_true, arraysize(expected_true)); | |
892 } | |
893 | |
894 TEST_F(HistoryURLProviderTest, CullSearchResults) { | |
895 // Set up a default search engine. | |
896 TemplateURLData data; | |
897 data.SetShortName(ASCIIToUTF16("TestEngine")); | |
898 data.SetKeyword(ASCIIToUTF16("TestEngine")); | |
899 data.SetURL("http://testsearch.com/?q={searchTerms}"); | |
900 TemplateURLService* template_url_service = | |
901 TemplateURLServiceFactory::GetForProfile(profile_.get()); | |
902 TemplateURL* template_url = new TemplateURL(data); | |
903 template_url_service->Add(template_url); | |
904 template_url_service->SetUserSelectedDefaultSearchProvider(template_url); | |
905 template_url_service->Load(); | |
906 | |
907 // URLs we will be using, plus the visit counts they will initially get | |
908 // (the redirect set below will also increment the visit counts). We want | |
909 // the results to be in A,B,C order. Note also that our visit counts are | |
910 // all high enough so that domain synthesizing won't get triggered. | |
911 struct TestCase { | |
912 const char* url; | |
913 int count; | |
914 } test_cases[] = { | |
915 {"https://testsearch.com/", 30}, | |
916 {"https://testsearch.com/?q=foobar", 20}, | |
917 {"http://foobar.com/", 10} | |
918 }; | |
919 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
920 history_service_->AddPageWithDetails(GURL(test_cases[i].url), | |
921 base::UTF8ToUTF16("Title"), test_cases[i].count, test_cases[i].count, | |
922 Time::Now(), false, history::SOURCE_BROWSED); | |
923 } | |
924 | |
925 // We should not see search URLs when typing a previously used query. | |
926 const UrlAndLegalDefault expected_when_searching_query[] = { | |
927 { test_cases[2].url, false } | |
928 }; | |
929 RunTest(ASCIIToUTF16("foobar"), std::string(), true, | |
930 expected_when_searching_query, arraysize(expected_when_searching_query)); | |
931 | |
932 // We should not see search URLs when typing the search engine name. | |
933 const UrlAndLegalDefault expected_when_searching_site[] = { | |
934 { test_cases[0].url, false } | |
935 }; | |
936 RunTest(ASCIIToUTF16("testsearch"), std::string(), true, | |
937 expected_when_searching_site, arraysize(expected_when_searching_site)); | |
938 } | |
939 | |
940 TEST_F(HistoryURLProviderTest, SuggestExactInput) { | |
941 const size_t npos = std::string::npos; | |
942 struct TestCase { | |
943 // Inputs: | |
944 const char* input; | |
945 bool trim_http; | |
946 // Expected Outputs: | |
947 const char* contents; | |
948 // Offsets of the ACMatchClassifications, terminated by npos. | |
949 size_t offsets[3]; | |
950 // The index of the ACMatchClassification that should have the MATCH bit | |
951 // set, npos if no ACMatchClassification should have the MATCH bit set. | |
952 size_t match_classification_index; | |
953 } test_cases[] = { | |
954 { "http://www.somesite.com", false, | |
955 "http://www.somesite.com", {0, npos, npos}, 0 }, | |
956 { "www.somesite.com", true, | |
957 "www.somesite.com", {0, npos, npos}, 0 }, | |
958 { "www.somesite.com", false, | |
959 "http://www.somesite.com", {0, 7, npos}, 1 }, | |
960 { "somesite.com", true, | |
961 "somesite.com", {0, npos, npos}, 0 }, | |
962 { "somesite.com", false, | |
963 "http://somesite.com", {0, 7, npos}, 1 }, | |
964 { "w", true, | |
965 "w", {0, npos, npos}, 0 }, | |
966 { "w", false, | |
967 "http://w", {0, 7, npos}, 1 }, | |
968 { "w.com", true, | |
969 "w.com", {0, npos, npos}, 0 }, | |
970 { "w.com", false, | |
971 "http://w.com", {0, 7, npos}, 1 }, | |
972 { "www.w.com", true, | |
973 "www.w.com", {0, npos, npos}, 0 }, | |
974 { "www.w.com", false, | |
975 "http://www.w.com", {0, 7, npos}, 1 }, | |
976 { "view-source:w", true, | |
977 "view-source:w", {0, npos, npos}, 0 }, | |
978 { "view-source:www.w.com/", true, | |
979 "view-source:www.w.com", {0, npos, npos}, npos }, | |
980 { "view-source:www.w.com/", false, | |
981 "view-source:http://www.w.com", {0, npos, npos}, npos }, | |
982 { "view-source:http://www.w.com/", false, | |
983 "view-source:http://www.w.com", {0, npos, npos}, 0 }, | |
984 { " view-source:", true, | |
985 "view-source:", {0, npos, npos}, 0 }, | |
986 { "http:////////w.com", false, | |
987 "http://w.com", {0, npos, npos}, npos }, | |
988 { " http:////////www.w.com", false, | |
989 "http://www.w.com", {0, npos, npos}, npos }, | |
990 { "http:a///www.w.com", false, | |
991 "http://a///www.w.com", {0, npos, npos}, npos }, | |
992 { "mailto://a@b.com", true, | |
993 "mailto://a@b.com", {0, npos, npos}, 0 }, | |
994 { "mailto://a@b.com", false, | |
995 "mailto://a@b.com", {0, npos, npos}, 0 }, | |
996 }; | |
997 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
998 SCOPED_TRACE(testing::Message() << "Index " << i << " input: " | |
999 << test_cases[i].input << ", trim_http: " | |
1000 << test_cases[i].trim_http); | |
1001 | |
1002 AutocompleteInput input( | |
1003 ASCIIToUTF16(test_cases[i].input), base::string16::npos, std::string(), | |
1004 GURL("about:blank"), metrics::OmniboxEventProto::INVALID_SPEC, false, | |
1005 false, true, true, false, | |
1006 ChromeAutocompleteSchemeClassifier(profile_.get())); | |
1007 AutocompleteMatch match(autocomplete_->SuggestExactInput( | |
1008 input, input.canonicalized_url(), test_cases[i].trim_http)); | |
1009 EXPECT_EQ(ASCIIToUTF16(test_cases[i].contents), match.contents); | |
1010 for (size_t match_index = 0; match_index < match.contents_class.size(); | |
1011 ++match_index) { | |
1012 EXPECT_EQ(test_cases[i].offsets[match_index], | |
1013 match.contents_class[match_index].offset); | |
1014 EXPECT_EQ(ACMatchClassification::URL | | |
1015 (match_index == test_cases[i].match_classification_index ? | |
1016 ACMatchClassification::MATCH : 0), | |
1017 match.contents_class[match_index].style); | |
1018 } | |
1019 EXPECT_EQ(npos, test_cases[i].offsets[match.contents_class.size()]); | |
1020 } | |
1021 } | |
1022 | |
1023 TEST_F(HistoryURLProviderTest, HUPScoringExperiment) { | |
1024 HUPScoringParams max_2000_no_time_decay; | |
1025 max_2000_no_time_decay.typed_count_buckets.buckets().push_back( | |
1026 std::make_pair(0.0, 2000)); | |
1027 HUPScoringParams max_1250_no_time_decay; | |
1028 max_1250_no_time_decay.typed_count_buckets.buckets().push_back( | |
1029 std::make_pair(0.0, 1250)); | |
1030 HUPScoringParams max_1000_no_time_decay; | |
1031 max_1000_no_time_decay.typed_count_buckets.buckets().push_back( | |
1032 std::make_pair(0.0, 1000)); | |
1033 | |
1034 HUPScoringParams max_1100_with_time_decay_and_max_cap; | |
1035 max_1100_with_time_decay_and_max_cap.typed_count_buckets. | |
1036 set_relevance_cap(1400); | |
1037 max_1100_with_time_decay_and_max_cap.typed_count_buckets. | |
1038 set_half_life_days(16); | |
1039 max_1100_with_time_decay_and_max_cap.typed_count_buckets.buckets().push_back( | |
1040 std::make_pair(0.5, 1100)); | |
1041 max_1100_with_time_decay_and_max_cap.typed_count_buckets.buckets().push_back( | |
1042 std::make_pair(0.24, 200)); | |
1043 max_1100_with_time_decay_and_max_cap.typed_count_buckets.buckets().push_back( | |
1044 std::make_pair(0.0, 100)); | |
1045 | |
1046 HUPScoringParams max_1100_visit_typed_decays; | |
1047 max_1100_visit_typed_decays.typed_count_buckets.set_half_life_days(16); | |
1048 max_1100_visit_typed_decays.typed_count_buckets.buckets().push_back( | |
1049 std::make_pair(0.5, 1100)); | |
1050 max_1100_visit_typed_decays.typed_count_buckets.buckets().push_back( | |
1051 std::make_pair(0.0, 100)); | |
1052 max_1100_visit_typed_decays.visited_count_buckets.set_half_life_days(16); | |
1053 max_1100_visit_typed_decays.visited_count_buckets.buckets().push_back( | |
1054 std::make_pair(0.5, 550)); | |
1055 max_1100_visit_typed_decays.visited_count_buckets.buckets().push_back( | |
1056 std::make_pair(0.0, 50)); | |
1057 | |
1058 const int kMaxMatches = 3; | |
1059 struct TestCase { | |
1060 const char* input; | |
1061 HUPScoringParams scoring_params; | |
1062 struct ExpectedMatch { | |
1063 const char* url; | |
1064 int control_relevance; | |
1065 int experiment_relevance; | |
1066 }; | |
1067 ExpectedMatch matches[kMaxMatches]; | |
1068 } test_cases[] = { | |
1069 // Max score 2000 -> no demotion. | |
1070 { "7.com/1", max_2000_no_time_decay, | |
1071 {{"7.com/1a", 1413, 1413}, {NULL, 0, 0}, {NULL, 0, 0}} }, | |
1072 | |
1073 // Limit score to 1250/1000 and make sure that the top match is unchanged. | |
1074 { "7.com/1", max_1250_no_time_decay, | |
1075 {{"7.com/1a", 1413, 1413}, {NULL, 0, 0}, {NULL, 0, 0}} }, | |
1076 { "7.com/2", max_1250_no_time_decay, | |
1077 {{"7.com/2a", 1413, 1413}, {"7.com/2b", 1412, 1250}, {NULL, 0, 0}} }, | |
1078 { "7.com/4", max_1000_no_time_decay, | |
1079 {{"7.com/4", 1203, 1203}, {"7.com/4a", 1202, 1000}, | |
1080 {"7.com/4b", 1201, 999}} }, | |
1081 | |
1082 // Max relevance cap is 1400 and half-life is 16 days. | |
1083 { "7.com/1", max_1100_with_time_decay_and_max_cap, | |
1084 {{"7.com/1a", 1413, 1413}, {NULL, 0, 0}, {NULL, 0, 0}} }, | |
1085 { "7.com/4", max_1100_with_time_decay_and_max_cap, | |
1086 {{"7.com/4", 1203, 1203}, {"7.com/4a", 1202, 200}, | |
1087 {"7.com/4b", 1201, 100}} }, | |
1088 | |
1089 // Max relevance cap is 1400 and half-life is 16 days for both visit/typed. | |
1090 { "7.com/5", max_1100_visit_typed_decays, | |
1091 {{"7.com/5", 1203, 1203}, {"7.com/5a", 1202, 50}, {NULL, 0, 0}} }, | |
1092 }; | |
1093 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
1094 SCOPED_TRACE(test_cases[i].input); | |
1095 UrlAndLegalDefault output[kMaxMatches]; | |
1096 int max_matches; | |
1097 for (max_matches = 0; max_matches < kMaxMatches; ++max_matches) { | |
1098 if (test_cases[i].matches[max_matches].url == NULL) | |
1099 break; | |
1100 output[max_matches].url = | |
1101 url_formatter::FixupURL(test_cases[i].matches[max_matches].url, | |
1102 std::string()) | |
1103 .spec(); | |
1104 output[max_matches].allowed_to_be_default_match = true; | |
1105 } | |
1106 autocomplete_->scoring_params_ = test_cases[i].scoring_params; | |
1107 | |
1108 // Test the experiment (scoring enabled). When scoring is disabled, it uses | |
1109 // the default experimental scoring. | |
1110 autocomplete_->scoring_params_.experimental_scoring_enabled = true; | |
1111 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16(test_cases[i].input), | |
1112 std::string(), false, output, max_matches)); | |
1113 for (int j = 0; j < max_matches; ++j) { | |
1114 EXPECT_EQ(test_cases[i].matches[j].experiment_relevance, | |
1115 matches_[j].relevance); | |
1116 } | |
1117 } | |
1118 } | |
OLD | NEW |