| 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 |