history_url_provider_unittest.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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 "chrome/browser/autocomplete/history_url_provider.h" 6 7#include <algorithm> 8 9#include "base/message_loop.h" 10#include "base/path_service.h" 11#include "base/string_util.h" 12#include "base/time.h" 13#include "base/utf_string_conversions.h" 14#include "chrome/browser/autocomplete/autocomplete_match.h" 15#include "chrome/browser/autocomplete/autocomplete_provider.h" 16#include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 17#include "chrome/browser/autocomplete/history_quick_provider.h" 18#include "chrome/browser/history/history_service.h" 19#include "chrome/browser/history/history_service_factory.h" 20#include "chrome/browser/net/url_fixer_upper.h" 21#include "chrome/browser/search_engines/template_url.h" 22#include "chrome/browser/search_engines/template_url_service.h" 23#include "chrome/browser/search_engines/template_url_service_factory.h" 24#include "chrome/test/base/testing_browser_process.h" 25#include "chrome/test/base/testing_profile.h" 26#include "content/public/test/test_browser_thread.h" 27#include "testing/gtest/include/gtest/gtest.h" 28 29using base::Time; 30using base::TimeDelta; 31 32using content::BrowserThread; 33 34struct TestURLInfo { 35 const char* url; 36 const char* title; 37 int visit_count; 38 int typed_count; 39} test_db[] = { 40 {"http://www.google.com/", "Google", 3, 3}, 41 42 // High-quality pages should get a host synthesized as a lower-quality match. 43 {"http://slashdot.org/favorite_page.html", "Favorite page", 200, 100}, 44 45 // Less popular pages should have hosts synthesized as higher-quality 46 // matches. 47 {"http://kerneltrap.org/not_very_popular.html", "Less popular", 4, 0}, 48 49 // Unpopular pages should not appear in the results at all. 50 {"http://freshmeat.net/unpopular.html", "Unpopular", 1, 0}, 51 52 // If a host has a match, we should pick it up during host synthesis. 53 {"http://news.google.com/?ned=us&topic=n", "Google News - U.S.", 2, 2}, 54 {"http://news.google.com/", "Google News", 1, 1}, 55 56 // Matches that are normally not inline-autocompletable should be 57 // autocompleted if they are shorter substitutes for longer matches that would 58 // have been inline autocompleted. 59 {"http://synthesisatest.com/foo/", "Test A", 1, 1}, 60 {"http://synthesisbtest.com/foo/", "Test B", 1, 1}, 61 {"http://synthesisbtest.com/foo/bar.html", "Test B Bar", 2, 2}, 62 63 // Suggested short URLs must be "good enough" and must match user input. 64 {"http://foo.com/", "Dir", 5, 5}, 65 {"http://foo.com/dir/", "Dir", 2, 2}, 66 {"http://foo.com/dir/another/", "Dir", 5, 1}, 67 {"http://foo.com/dir/another/again/", "Dir", 10, 0}, 68 {"http://foo.com/dir/another/again/myfile.html", "File", 10, 2}, 69 70 // We throw in a lot of extra URLs here to make sure we're testing the 71 // history database's query, not just the autocomplete provider. 72 {"http://startest.com/y/a", "A", 2, 2}, 73 {"http://startest.com/y/b", "B", 5, 2}, 74 {"http://startest.com/x/c", "C", 5, 2}, 75 {"http://startest.com/x/d", "D", 5, 5}, 76 {"http://startest.com/y/e", "E", 4, 2}, 77 {"http://startest.com/y/f", "F", 3, 2}, 78 {"http://startest.com/y/g", "G", 3, 2}, 79 {"http://startest.com/y/h", "H", 3, 2}, 80 {"http://startest.com/y/i", "I", 3, 2}, 81 {"http://startest.com/y/j", "J", 3, 2}, 82 {"http://startest.com/y/k", "K", 3, 2}, 83 {"http://startest.com/y/l", "L", 3, 2}, 84 {"http://startest.com/y/m", "M", 3, 2}, 85 86 // A file: URL is useful for testing that fixup does the right thing w.r.t. 87 // the number of trailing slashes on the user's input. 88 {"file:///C:/foo.txt", "", 2, 2}, 89 90 // Results with absurdly high typed_counts so that very generic queries like 91 // "http" will give consistent results even if more data is added above. 92 {"http://bogussite.com/a", "Bogus A", 10002, 10000}, 93 {"http://bogussite.com/b", "Bogus B", 10001, 10000}, 94 {"http://bogussite.com/c", "Bogus C", 10000, 10000}, 95 96 // Domain name with number. 97 {"http://www.17173.com/", "Domain with number", 3, 3}, 98 99 // URLs to test exact-matching behavior. 100 {"http://go/", "Intranet URL", 1, 1}, 101 {"http://gooey/", "Intranet URL 2", 5, 5}, 102 103 // URLs for testing offset adjustment. 104 {"http://www.\xEA\xB5\x90\xEC\x9C\xA1.kr/", "Korean", 2, 2}, 105 {"http://spaces.com/path%20with%20spaces/foo.html", "Spaces", 2, 2}, 106 {"http://ms/c++%20style%20guide", "Style guide", 2, 2}, 107 108 // URLs for testing ctrl-enter behavior. 109 {"http://binky/", "Intranet binky", 2, 2}, 110 {"http://winky/", "Intranet winky", 2, 2}, 111 {"http://www.winky.com/", "Internet winky", 5, 0}, 112 113 // URLs used by EmptyVisits. 114 {"http://pandora.com/", "Pandora", 2, 2}, 115 // This entry is explicitly added more recently than 116 // history::kLowQualityMatchAgeLimitInDays. 117 // {"http://p/", "p", 0, 0}, 118 119 // For intranet based tests. 120 {"http://intra/one", "Intranet", 2, 2}, 121 {"http://intra/two", "Intranet two", 1, 1}, 122 {"http://intra/three", "Intranet three", 2, 2}, 123 {"http://moo/bar", "Intranet moo", 1, 1}, 124 {"http://typedhost/typedpath", "Intranet typed", 1, 1}, 125 {"http://typedhost/untypedpath", "Intranet untyped", 1, 0}, 126 127 {"http://x.com/one", "Internet", 2, 2}, 128 {"http://x.com/two", "Internet two", 1, 1}, 129 {"http://x.com/three", "Internet three", 2, 2}, 130}; 131 132class HistoryURLProviderTest : public testing::Test, 133 public AutocompleteProviderListener { 134 public: 135 HistoryURLProviderTest() 136 : ui_thread_(BrowserThread::UI, &message_loop_), 137 file_thread_(BrowserThread::FILE, &message_loop_), 138 sort_matches_(false) { 139 HistoryQuickProvider::set_disabled(true); 140 } 141 142 virtual ~HistoryURLProviderTest() { 143 HistoryQuickProvider::set_disabled(false); 144 } 145 146 // AutocompleteProviderListener: 147 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 148 149 protected: 150 static ProfileKeyedService* CreateTemplateURLService( 151 content::BrowserContext* profile) { 152 return new TemplateURLService(static_cast<Profile*>(profile)); 153 } 154 155 // testing::Test 156 virtual void SetUp() { 157 SetUpImpl(false); 158 } 159 virtual void TearDown(); 160 161 // Does the real setup. 162 void SetUpImpl(bool no_db); 163 164 // Fills test data into the history system. 165 void FillData(); 166 167 // Runs an autocomplete query on |text| and checks to see that the returned 168 // results' destination URLs match those provided. Also allows checking 169 // that the input type was identified correctly. 170 void RunTest(const string16 text, 171 const string16& desired_tld, 172 bool prevent_inline_autocomplete, 173 const std::string* expected_urls, 174 size_t num_results, 175 AutocompleteInput::Type* identified_input_type); 176 177 // A version of the above without the final |type| output parameter. 178 void RunTest(const string16 text, 179 const string16& desired_tld, 180 bool prevent_inline_autocomplete, 181 const std::string* expected_urls, 182 size_t num_results) { 183 AutocompleteInput::Type type; 184 return RunTest(text, desired_tld, prevent_inline_autocomplete, 185 expected_urls, num_results, &type); 186 } 187 188 void RunAdjustOffsetTest(const string16 text, size_t expected_offset); 189 190 MessageLoopForUI message_loop_; 191 content::TestBrowserThread ui_thread_; 192 content::TestBrowserThread file_thread_; 193 ACMatches matches_; 194 scoped_ptr<TestingProfile> profile_; 195 HistoryService* history_service_; 196 scoped_refptr<HistoryURLProvider> autocomplete_; 197 // Should the matches be sorted and duplicates removed? 198 bool sort_matches_; 199}; 200 201class HistoryURLProviderTestNoDB : public HistoryURLProviderTest { 202 protected: 203 virtual void SetUp() { 204 SetUpImpl(true); 205 } 206}; 207 208void HistoryURLProviderTest::OnProviderUpdate(bool updated_matches) { 209 if (autocomplete_->done()) 210 MessageLoop::current()->Quit(); 211} 212 213void HistoryURLProviderTest::SetUpImpl(bool no_db) { 214 profile_.reset(new TestingProfile()); 215 profile_->CreateHistoryService(true, no_db); 216 history_service_ = 217 HistoryServiceFactory::GetForProfile(profile_.get(), 218 Profile::EXPLICIT_ACCESS); 219 220 autocomplete_ = new HistoryURLProvider(this, profile_.get(), "en-US,en,ko"); 221 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 222 profile_.get(), &HistoryURLProviderTest::CreateTemplateURLService); 223 FillData(); 224} 225 226void HistoryURLProviderTest::TearDown() { 227 autocomplete_ = NULL; 228} 229 230void HistoryURLProviderTest::FillData() { 231 // All visits are a long time ago (some tests require this since we do some 232 // special logic for things visited very recently). Note that this time must 233 // be more recent than the "archived history" threshold for the data to go 234 // into the main database. 235 // 236 // TODO(brettw) It would be nice if we could test this behavior, in which 237 // case the time would be specifed in the test_db structure. 238 Time visit_time = Time::Now() - TimeDelta::FromDays(80); 239 240 for (size_t i = 0; i < arraysize(test_db); ++i) { 241 const TestURLInfo& cur = test_db[i]; 242 const GURL current_url(cur.url); 243 history_service_->AddPageWithDetails(current_url, UTF8ToUTF16(cur.title), 244 cur.visit_count, cur.typed_count, 245 visit_time, false, 246 history::SOURCE_BROWSED); 247 } 248 249 history_service_->AddPageWithDetails( 250 GURL("http://p/"), UTF8ToUTF16("p"), 0, 0, 251 Time::Now() - 252 TimeDelta::FromDays(history::kLowQualityMatchAgeLimitInDays - 1), 253 false, history::SOURCE_BROWSED); 254} 255 256void HistoryURLProviderTest::RunTest( 257 const string16 text, 258 const string16& desired_tld, 259 bool prevent_inline_autocomplete, 260 const std::string* expected_urls, 261 size_t num_results, 262 AutocompleteInput::Type* identified_input_type) { 263 AutocompleteInput input(text, string16::npos, desired_tld, GURL(), 264 prevent_inline_autocomplete, false, true, 265 AutocompleteInput::ALL_MATCHES); 266 *identified_input_type = input.type(); 267 autocomplete_->Start(input, false); 268 if (!autocomplete_->done()) 269 MessageLoop::current()->Run(); 270 271 matches_ = autocomplete_->matches(); 272 if (sort_matches_) { 273 for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) 274 i->ComputeStrippedDestinationURL(profile_.get()); 275 std::sort(matches_.begin(), matches_.end(), 276 &AutocompleteMatch::DestinationSortFunc); 277 matches_.erase(std::unique(matches_.begin(), matches_.end(), 278 &AutocompleteMatch::DestinationsEqual), 279 matches_.end()); 280 std::sort(matches_.begin(), matches_.end(), 281 &AutocompleteMatch::MoreRelevant); 282 } 283 ASSERT_EQ(num_results, matches_.size()) << "Input text: " << text 284 << "\nTLD: \"" << desired_tld << "\""; 285 for (size_t i = 0; i < num_results; ++i) 286 EXPECT_EQ(expected_urls[i], matches_[i].destination_url.spec()); 287} 288 289void HistoryURLProviderTest::RunAdjustOffsetTest(const string16 text, 290 size_t expected_offset) { 291 AutocompleteInput input(text, string16::npos, string16(), GURL(), false, 292 false, true, AutocompleteInput::ALL_MATCHES); 293 autocomplete_->Start(input, false); 294 if (!autocomplete_->done()) 295 MessageLoop::current()->Run(); 296 297 matches_ = autocomplete_->matches(); 298 ASSERT_GE(matches_.size(), 1U) << "Input text: " << text; 299 EXPECT_EQ(expected_offset, matches_[0].inline_autocomplete_offset); 300} 301 302TEST_F(HistoryURLProviderTest, PromoteShorterURLs) { 303 // Test that hosts get synthesized below popular pages. 304 const std::string expected_nonsynth[] = { 305 "http://slashdot.org/favorite_page.html", 306 "http://slashdot.org/", 307 }; 308 RunTest(ASCIIToUTF16("slash"), string16(), true, expected_nonsynth, 309 arraysize(expected_nonsynth)); 310 311 // Test that hosts get synthesized above less popular pages. 312 const std::string expected_synth[] = { 313 "http://kerneltrap.org/", 314 "http://kerneltrap.org/not_very_popular.html", 315 }; 316 RunTest(ASCIIToUTF16("kernel"), string16(), true, expected_synth, 317 arraysize(expected_synth)); 318 319 // Test that unpopular pages are ignored completely. 320 RunTest(ASCIIToUTF16("fresh"), string16(), true, NULL, 0); 321 322 // Test that if we create or promote shorter suggestions that would not 323 // normally be inline autocompletable, we make them inline autocompletable if 324 // the original suggestion (that we replaced as "top") was inline 325 // autocompletable. 326 const std::string expected_synthesisa[] = { 327 "http://synthesisatest.com/", 328 "http://synthesisatest.com/foo/", 329 }; 330 RunTest(ASCIIToUTF16("synthesisa"), string16(), false, expected_synthesisa, 331 arraysize(expected_synthesisa)); 332 EXPECT_LT(matches_.front().relevance, 1200); 333 const std::string expected_synthesisb[] = { 334 "http://synthesisbtest.com/foo/", 335 "http://synthesisbtest.com/foo/bar.html", 336 }; 337 RunTest(ASCIIToUTF16("synthesisb"), string16(), false, expected_synthesisb, 338 arraysize(expected_synthesisb)); 339 EXPECT_GE(matches_.front().relevance, 1410); 340 341 // Test that if we have a synthesized host that matches a suggestion, they 342 // get combined into one. 343 const std::string expected_combine[] = { 344 "http://news.google.com/", 345 "http://news.google.com/?ned=us&topic=n", 346 }; 347 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("news"), string16(), true, 348 expected_combine, arraysize(expected_combine))); 349 // The title should also have gotten set properly on the host for the 350 // synthesized one, since it was also in the results. 351 EXPECT_EQ(ASCIIToUTF16("Google News"), matches_.front().description); 352 353 // Test that short URL matching works correctly as the user types more 354 // (several tests): 355 // The entry for foo.com is the best of all five foo.com* entries. 356 const std::string short_1[] = { 357 "http://foo.com/", 358 "http://foo.com/dir/another/again/myfile.html", 359 "http://foo.com/dir/", 360 }; 361 RunTest(ASCIIToUTF16("foo"), string16(), true, short_1, arraysize(short_1)); 362 363 // When the user types the whole host, make sure we don't get two results for 364 // it. 365 const std::string short_2[] = { 366 "http://foo.com/", 367 "http://foo.com/dir/another/again/myfile.html", 368 "http://foo.com/dir/", 369 "http://foo.com/dir/another/", 370 }; 371 RunTest(ASCIIToUTF16("foo.com"), string16(), true, short_2, 372 arraysize(short_2)); 373 RunTest(ASCIIToUTF16("foo.com/"), string16(), true, short_2, 374 arraysize(short_2)); 375 376 // The filename is the second best of the foo.com* entries, but there is a 377 // shorter URL that's "good enough". The host doesn't match the user input 378 // and so should not appear. 379 const std::string short_3[] = { 380 "http://foo.com/d", 381 "http://foo.com/dir/another/", 382 "http://foo.com/dir/another/again/myfile.html", 383 "http://foo.com/dir/", 384 }; 385 RunTest(ASCIIToUTF16("foo.com/d"), string16(), true, short_3, 386 arraysize(short_3)); 387 388 // We shouldn't promote shorter URLs than the best if they're not good 389 // enough. 390 const std::string short_4[] = { 391 "http://foo.com/dir/another/a", 392 "http://foo.com/dir/another/again/myfile.html", 393 "http://foo.com/dir/another/again/", 394 }; 395 RunTest(ASCIIToUTF16("foo.com/dir/another/a"), string16(), true, short_4, 396 arraysize(short_4)); 397 398 // Exact matches should always be best no matter how much more another match 399 // has been typed. 400 const std::string short_5a[] = { 401 "http://gooey/", 402 "http://www.google.com/", 403 "http://go/", 404 }; 405 const std::string short_5b[] = { 406 "http://go/", 407 "http://gooey/", 408 "http://www.google.com/", 409 }; 410 RunTest(ASCIIToUTF16("g"), string16(), false, short_5a, arraysize(short_5a)); 411 RunTest(ASCIIToUTF16("go"), string16(), false, short_5b, arraysize(short_5b)); 412} 413 414TEST_F(HistoryURLProviderTest, CullRedirects) { 415 // URLs we will be using, plus the visit counts they will initially get 416 // (the redirect set below will also increment the visit counts). We want 417 // the results to be in A,B,C order. Note also that our visit counts are 418 // all high enough so that domain synthesizing won't get triggered. 419 struct TestCase { 420 const char* url; 421 int count; 422 } test_cases[] = { 423 {"http://redirects/A", 30}, 424 {"http://redirects/B", 20}, 425 {"http://redirects/C", 10} 426 }; 427 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) { 428 history_service_->AddPageWithDetails(GURL(test_cases[i].url), 429 UTF8ToUTF16("Title"), test_cases[i].count, test_cases[i].count, 430 Time::Now(), false, history::SOURCE_BROWSED); 431 } 432 433 // Create a B->C->A redirect chain, but set the visit counts such that they 434 // will appear in A,B,C order in the results. The autocomplete query will 435 // search for the most recent visit when looking for redirects, so this will 436 // be found even though the previous visits had no redirects. 437 history::RedirectList redirects_to_a; 438 redirects_to_a.push_back(GURL(test_cases[1].url)); 439 redirects_to_a.push_back(GURL(test_cases[2].url)); 440 redirects_to_a.push_back(GURL(test_cases[0].url)); 441 history_service_->AddPage(GURL(test_cases[0].url), base::Time::Now(), 442 NULL, 0, GURL(), redirects_to_a, content::PAGE_TRANSITION_TYPED, 443 history::SOURCE_BROWSED, true); 444 445 // Because all the results are part of a redirect chain with other results, 446 // all but the first one (A) should be culled. We should get the default 447 // "what you typed" result, plus this one. 448 const string16 typing(ASCIIToUTF16("http://redirects/")); 449 const std::string expected_results[] = { 450 UTF16ToUTF8(typing), 451 test_cases[0].url, 452 }; 453 RunTest(typing, string16(), true, expected_results, 454 arraysize(expected_results)); 455} 456 457TEST_F(HistoryURLProviderTest, WhatYouTyped) { 458 // Make sure we suggest a What You Typed match at the right times. 459 RunTest(ASCIIToUTF16("wytmatch"), string16(), false, NULL, 0); 460 RunTest(ASCIIToUTF16("wytmatch foo bar"), string16(), false, NULL, 0); 461 RunTest(ASCIIToUTF16("wytmatch+foo+bar"), string16(), false, NULL, 0); 462 RunTest(ASCIIToUTF16("wytmatch+foo+bar.com"), string16(), false, NULL, 0); 463 464 const std::string results_1[] = {"http://www.wytmatch.com/"}; 465 RunTest(ASCIIToUTF16("wytmatch"), ASCIIToUTF16("com"), false, results_1, 466 arraysize(results_1)); 467 468 const std::string results_2[] = {"http://wytmatch%20foo%20bar/"}; 469 RunTest(ASCIIToUTF16("http://wytmatch foo bar"), string16(), false, results_2, 470 arraysize(results_2)); 471 472 const std::string results_3[] = {"https://wytmatch%20foo%20bar/"}; 473 RunTest(ASCIIToUTF16("https://wytmatch foo bar"), string16(), false, 474 results_3, arraysize(results_3)); 475} 476 477TEST_F(HistoryURLProviderTest, Fixup) { 478 // Test for various past crashes we've had. 479 RunTest(ASCIIToUTF16("\\"), string16(), false, NULL, 0); 480 RunTest(ASCIIToUTF16("#"), string16(), false, NULL, 0); 481 RunTest(ASCIIToUTF16("%20"), string16(), false, NULL, 0); 482 const std::string fixup_crash[] = {"http://%EF%BD%A5@s/"}; 483 RunTest(WideToUTF16(L"\uff65@s"), string16(), false, fixup_crash, 484 arraysize(fixup_crash)); 485 RunTest(WideToUTF16(L"\u2015\u2015@ \uff7c"), string16(), false, NULL, 0); 486 487 // Fixing up "file:" should result in an inline autocomplete offset of just 488 // after "file:", not just after "file://". 489 const string16 input_1(ASCIIToUTF16("file:")); 490 const std::string fixup_1[] = {"file:///C:/foo.txt"}; 491 ASSERT_NO_FATAL_FAILURE(RunTest(input_1, string16(), false, fixup_1, 492 arraysize(fixup_1))); 493 EXPECT_EQ(input_1.length(), matches_.front().inline_autocomplete_offset); 494 495 // Fixing up "http:/" should result in an inline autocomplete offset of just 496 // after "http:/", not just after "http:". 497 const string16 input_2(ASCIIToUTF16("http:/")); 498 const std::string fixup_2[] = { 499 "http://bogussite.com/a", 500 "http://bogussite.com/b", 501 "http://bogussite.com/c", 502 }; 503 ASSERT_NO_FATAL_FAILURE(RunTest(input_2, string16(), false, fixup_2, 504 arraysize(fixup_2))); 505 EXPECT_EQ(input_2.length(), matches_.front().inline_autocomplete_offset); 506 507 // Adding a TLD to a small number like "56" should result in "www.56.com" 508 // rather than "0.0.0.56.com". 509 const std::string fixup_3[] = {"http://www.56.com/"}; 510 RunTest(ASCIIToUTF16("56"), ASCIIToUTF16("com"), true, fixup_3, 511 arraysize(fixup_3)); 512 513 // An input looks like a IP address like "127.0.0.1" should result in 514 // "http://127.0.0.1/". 515 const std::string fixup_4[] = {"http://127.0.0.1/"}; 516 RunTest(ASCIIToUTF16("127.0.0.1"), string16(), false, fixup_4, 517 arraysize(fixup_4)); 518 519 // An number "17173" should result in "http://www.17173.com/" in db. 520 const std::string fixup_5[] = {"http://www.17173.com/"}; 521 RunTest(ASCIIToUTF16("17173"), string16(), false, fixup_5, 522 arraysize(fixup_5)); 523} 524 525TEST_F(HistoryURLProviderTest, AdjustOffset) { 526 RunAdjustOffsetTest(WideToUTF16(L"http://www.\uAD50\uC721"), 13); 527 RunAdjustOffsetTest(ASCIIToUTF16("http://spaces.com/path%20with%20spa"), 31); 528 RunAdjustOffsetTest(ASCIIToUTF16("http://ms/c++ s"), 15); 529} 530 531// Make sure the results for the input 'p' don't change between the first and 532// second passes. 533TEST_F(HistoryURLProviderTest, EmptyVisits) { 534 // Wait for history to create the in memory DB. 535 profile_->BlockUntilHistoryProcessesPendingRequests(); 536 537 AutocompleteInput input(ASCIIToUTF16("p"), string16::npos, string16(), GURL(), 538 false, false, true, AutocompleteInput::ALL_MATCHES); 539 autocomplete_->Start(input, false); 540 // HistoryURLProvider shouldn't be done (waiting on async results). 541 EXPECT_FALSE(autocomplete_->done()); 542 543 // We should get back an entry for pandora. 544 matches_ = autocomplete_->matches(); 545 ASSERT_GT(matches_.size(), 0u); 546 EXPECT_EQ(GURL("http://pandora.com/"), matches_[0].destination_url); 547 int pandora_relevance = matches_[0].relevance; 548 549 // Run the message loop. When |autocomplete_| finishes the loop is quit. 550 MessageLoop::current()->Run(); 551 EXPECT_TRUE(autocomplete_->done()); 552 matches_ = autocomplete_->matches(); 553 ASSERT_GT(matches_.size(), 0u); 554 EXPECT_EQ(GURL("http://pandora.com/"), matches_[0].destination_url); 555 EXPECT_EQ(pandora_relevance, matches_[0].relevance); 556} 557 558TEST_F(HistoryURLProviderTestNoDB, NavigateWithoutDB) { 559 // Ensure that we will still produce matches for navigation when there is no 560 // database. 561 std::string navigation_1[] = {"http://test.com/"}; 562 RunTest(ASCIIToUTF16("test.com"), string16(), false, navigation_1, 563 arraysize(navigation_1)); 564 565 std::string navigation_2[] = {"http://slash/"}; 566 RunTest(ASCIIToUTF16("slash"), string16(), false, navigation_2, 567 arraysize(navigation_2)); 568 569 RunTest(ASCIIToUTF16("this is a query"), string16(), false, NULL, 0); 570} 571 572TEST_F(HistoryURLProviderTest, DontAutocompleteOnTrailingWhitespace) { 573 AutocompleteInput input(ASCIIToUTF16("slash "), string16::npos, string16(), 574 GURL(), false, false, true, 575 AutocompleteInput::ALL_MATCHES); 576 autocomplete_->Start(input, false); 577 if (!autocomplete_->done()) 578 MessageLoop::current()->Run(); 579 580 // None of the matches should attempt to autocomplete. 581 matches_ = autocomplete_->matches(); 582 for (size_t i = 0; i < matches_.size(); ++i) 583 EXPECT_EQ(string16::npos, matches_[i].inline_autocomplete_offset); 584} 585 586TEST_F(HistoryURLProviderTest, TreatEmailsAsSearches) { 587 // Visiting foo.com should not make this string be treated as a navigation. 588 // That means the result should be scored around 1200 ("what you typed") 589 // and not 1400+. 590 const std::string expected[] = {"http://user@foo.com/"}; 591 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("user@foo.com"), string16(), 592 false, expected, arraysize(expected))); 593 EXPECT_LE(1200, matches_[0].relevance); 594 EXPECT_LT(matches_[0].relevance, 1210); 595} 596 597TEST_F(HistoryURLProviderTest, IntranetURLsWithPaths) { 598 struct TestCase { 599 const char* input; 600 int relevance; 601 } test_cases[] = { 602 { "fooey", 0 }, 603 { "fooey/", 1200 }, // 1200 for URL would still navigate by default. 604 { "fooey/a", 1200 }, // 1200 for UNKNOWN would not. 605 { "fooey/a b", 1200 }, // Also UNKNOWN. 606 { "gooey", 1410 }, 607 { "gooey/", 1410 }, 608 { "gooey/a", 1400 }, 609 { "gooey/a b", 1400 }, 610 }; 611 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { 612 SCOPED_TRACE(test_cases[i].input); 613 if (test_cases[i].relevance == 0) { 614 RunTest(ASCIIToUTF16(test_cases[i].input), string16(), false, NULL, 0); 615 } else { 616 const std::string output[] = { 617 URLFixerUpper::FixupURL(test_cases[i].input, std::string()).spec() 618 }; 619 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16(test_cases[i].input), 620 string16(), false, output, arraysize(output))); 621 // Actual relevance should be at least what test_cases expects and 622 // and no more than 10 more. 623 EXPECT_LE(test_cases[i].relevance, matches_[0].relevance); 624 EXPECT_LT(matches_[0].relevance, test_cases[i].relevance + 10); 625 } 626 } 627} 628 629TEST_F(HistoryURLProviderTest, IntranetURLsWithRefs) { 630 struct TestCase { 631 const char* input; 632 int relevance; 633 AutocompleteInput::Type type; 634 } test_cases[] = { 635 { "gooey", 1410, AutocompleteInput::UNKNOWN }, 636 { "gooey/", 1410, AutocompleteInput::URL }, 637 { "gooey#", 1200, AutocompleteInput::UNKNOWN }, 638 { "gooey/#", 1200, AutocompleteInput::URL }, 639 { "gooey#foo", 1200, AutocompleteInput::UNKNOWN }, 640 { "gooey/#foo", 1200, AutocompleteInput::URL }, 641 { "gooey# foo", 1200, AutocompleteInput::UNKNOWN }, 642 { "gooey/# foo", 1200, AutocompleteInput::URL }, 643 }; 644 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { 645 SCOPED_TRACE(test_cases[i].input); 646 const std::string output[] = { 647 URLFixerUpper::FixupURL(test_cases[i].input, std::string()).spec() 648 }; 649 AutocompleteInput::Type type; 650 ASSERT_NO_FATAL_FAILURE( 651 RunTest(ASCIIToUTF16(test_cases[i].input), 652 string16(), false, output, arraysize(output), &type)); 653 // Actual relevance should be at least what test_cases expects and 654 // and no more than 10 more. 655 EXPECT_LE(test_cases[i].relevance, matches_[0].relevance); 656 EXPECT_LT(matches_[0].relevance, test_cases[i].relevance + 10); 657 // Input type should be what we expect. This is important because 658 // this provider counts on SearchProvider to give queries a relevance 659 // score >1200 for UNKNOWN inputs and <1200 for URL inputs. (That's 660 // already tested in search_provider_unittest.cc.) For this test 661 // here to test that the user sees the correct behavior, it needs 662 // to check that the input type was identified correctly. 663 EXPECT_EQ(test_cases[i].type, type); 664 } 665} 666 667// Makes sure autocompletion happens for intranet sites that have been 668// previoulsy visited. 669TEST_F(HistoryURLProviderTest, IntranetURLCompletion) { 670 sort_matches_ = true; 671 672 const std::string expected1[] = { 673 "http://intra/three", 674 "http://intra/two", 675 }; 676 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("intra/t"), string16(), false, 677 expected1, arraysize(expected1))); 678 EXPECT_LE(1410, matches_[0].relevance); 679 EXPECT_LT(matches_[0].relevance, 1420); 680 EXPECT_EQ(matches_[0].relevance - 1, matches_[1].relevance); 681 682 const std::string expected2[] = { 683 "http://moo/b", 684 "http://moo/bar", 685 }; 686 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("moo/b"), string16(), false, 687 expected2, arraysize(expected2))); 688 // The url what you typed match should be around 1400, otherwise the 689 // search what you typed match is going to be first. 690 EXPECT_LE(1400, matches_[0].relevance); 691 EXPECT_LT(matches_[0].relevance, 1410); 692 693 const std::string expected3[] = { 694 "http://intra/one", 695 "http://intra/three", 696 "http://intra/two", 697 }; 698 RunTest(ASCIIToUTF16("intra"), string16(), false, expected3, 699 arraysize(expected3)); 700 701 const std::string expected4[] = { 702 "http://intra/one", 703 "http://intra/three", 704 "http://intra/two", 705 }; 706 RunTest(ASCIIToUTF16("intra/"), string16(), false, expected4, 707 arraysize(expected4)); 708 709 const std::string expected5[] = { 710 "http://intra/one", 711 }; 712 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("intra/o"), string16(), false, 713 expected5, arraysize(expected5))); 714 EXPECT_LE(1410, matches_[0].relevance); 715 EXPECT_LT(matches_[0].relevance, 1420); 716 717 const std::string expected6[] = { 718 "http://intra/x", 719 }; 720 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("intra/x"), string16(), false, 721 expected6, arraysize(expected6))); 722 EXPECT_LE(1400, matches_[0].relevance); 723 EXPECT_LT(matches_[0].relevance, 1410); 724 725 const std::string expected7[] = { 726 "http://typedhost/untypedpath", 727 }; 728 ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16("typedhost/untypedpath"), 729 string16(), false, expected7, arraysize(expected7))); 730 EXPECT_LE(1400, matches_[0].relevance); 731 EXPECT_LT(matches_[0].relevance, 1410); 732} 733 734TEST_F(HistoryURLProviderTest, CrashDueToFixup) { 735 // This test passes if we don't crash. The results don't matter. 736 const char* const test_cases[] = { 737 "//c", 738 "\\@st" 739 }; 740 for (size_t i = 0; i < arraysize(test_cases); ++i) { 741 AutocompleteInput input(ASCIIToUTF16(test_cases[i]), string16::npos, 742 string16(), GURL(), false, false, true, 743 AutocompleteInput::ALL_MATCHES); 744 autocomplete_->Start(input, false); 745 if (!autocomplete_->done()) 746 MessageLoop::current()->Run(); 747 } 748} 749 750TEST_F(HistoryURLProviderTest, CullSearchResults) { 751 // Set up a default search engine. 752 TemplateURLData data; 753 data.SetKeyword(ASCIIToUTF16("TestEngine")); 754 data.SetURL("http://testsearch.com/?q={searchTerms}"); 755 TemplateURLService* template_url_service = 756 TemplateURLServiceFactory::GetForProfile(profile_.get()); 757 TemplateURL* template_url = new TemplateURL(profile_.get(), data); 758 template_url_service->Add(template_url); 759 template_url_service->SetDefaultSearchProvider(template_url); 760 template_url_service->Load(); 761 762 // URLs we will be using, plus the visit counts they will initially get 763 // (the redirect set below will also increment the visit counts). We want 764 // the results to be in A,B,C order. Note also that our visit counts are 765 // all high enough so that domain synthesizing won't get triggered. 766 struct TestCase { 767 const char* url; 768 int count; 769 } test_cases[] = { 770 {"https://testsearch.com/", 30}, 771 {"https://testsearch.com/?q=foobar", 20}, 772 {"http://foobar.com/", 10} 773 }; 774 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) { 775 history_service_->AddPageWithDetails(GURL(test_cases[i].url), 776 UTF8ToUTF16("Title"), test_cases[i].count, test_cases[i].count, 777 Time::Now(), false, history::SOURCE_BROWSED); 778 } 779 780 // We should not see search URLs when typing a previously used query. 781 const std::string expected_when_searching_query[] = { 782 test_cases[2].url 783 }; 784 RunTest(ASCIIToUTF16("foobar"), string16(), true, 785 expected_when_searching_query, arraysize(expected_when_searching_query)); 786 787 // We should not see search URLs when typing the search engine name. 788 const std::string expected_when_searching_site[] = { 789 test_cases[0].url 790 }; 791 RunTest(ASCIIToUTF16("testsearch"), string16(), true, 792 expected_when_searching_site, arraysize(expected_when_searching_site)); 793} 794