search_provider_unittest.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/search_provider.h" 6 7#include "base/run_loop.h" 8#include "base/string_util.h" 9#include "base/time.h" 10#include "base/utf_string_conversions.h" 11#include "build/build_config.h" 12#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 13#include "chrome/browser/autocomplete/autocomplete_controller.h" 14#include "chrome/browser/autocomplete/autocomplete_input.h" 15#include "chrome/browser/autocomplete/autocomplete_match.h" 16#include "chrome/browser/autocomplete/autocomplete_provider.h" 17#include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 18#include "chrome/browser/history/history.h" 19#include "chrome/browser/history/history_service_factory.h" 20#include "chrome/browser/instant/instant_controller.h" 21#include "chrome/browser/prefs/pref_service.h" 22#include "chrome/browser/search_engines/template_url.h" 23#include "chrome/browser/search_engines/template_url_service.h" 24#include "chrome/browser/search_engines/template_url_service_factory.h" 25#include "chrome/common/pref_names.h" 26#include "chrome/test/base/testing_browser_process.h" 27#include "chrome/test/base/testing_profile.h" 28#include "content/public/test/test_browser_thread.h" 29#include "net/url_request/test_url_fetcher_factory.h" 30#include "net/url_request/url_request_status.h" 31#include "testing/gtest/include/gtest/gtest.h" 32 33using content::BrowserThread; 34 35// The following environment is configured for these tests: 36// . The TemplateURL default_t_url_ is set as the default provider. 37// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This 38// TemplateURL has a valid suggest and search URL. 39// . The URL created by using the search term term1_ with default_t_url_ is 40// added to history. 41// . The URL created by using the search term keyword_term_ with keyword_t_url_ 42// is added to history. 43// . test_factory_ is set as the URLFetcherFactory. 44class SearchProviderTest : public testing::Test, 45 public AutocompleteProviderListener { 46 public: 47 SearchProviderTest() 48 : default_t_url_(NULL), 49 term1_(UTF8ToUTF16("term1")), 50 keyword_t_url_(NULL), 51 keyword_term_(UTF8ToUTF16("keyword")), 52 ui_thread_(BrowserThread::UI, &message_loop_), 53 io_thread_(BrowserThread::IO), 54 quit_when_done_(false) { 55 io_thread_.Start(); 56 } 57 58 // See description above class for what this registers. 59 virtual void SetUp(); 60 61 virtual void TearDown(); 62 63 protected: 64 // Adds a search for |term|, using the engine |t_url| to the history, and 65 // returns the URL for that search. 66 GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count); 67 68 // Looks for a match in |provider_| with |contents| equal to |contents|. 69 // Sets |match| to it if found. Returns whether |match| was set. 70 bool FindMatchWithContents(const string16& contents, 71 AutocompleteMatch* match); 72 73 // Looks for a match in |provider_| with destination |url|. Sets |match| to 74 // it if found. Returns whether |match| was set. 75 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match); 76 77 // AutocompleteProviderListener: 78 // If we're waiting for the provider to finish, this exits the message loop. 79 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 80 81 // Runs a nested message loop until provider_ is done. The message loop is 82 // exited by way of OnProviderUPdate. 83 void RunTillProviderDone(); 84 85 // Invokes Start on provider_, then runs all pending tasks. 86 void QueryForInput(const string16& text, 87 const string16& desired_tld, 88 bool prevent_inline_autocomplete); 89 90 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is 91 // non-NULL, sets it to the "what you typed" entry for |text|. 92 void QueryForInputAndSetWYTMatch(const string16& text, 93 AutocompleteMatch* wyt_match); 94 95 // Notifies the URLFetcher for the suggest query corresponding to the default 96 // search provider that it's done. 97 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE. 98 void FinishDefaultSuggestQuery(); 99 100 // See description above class for details of these fields. 101 TemplateURL* default_t_url_; 102 const string16 term1_; 103 GURL term1_url_; 104 TemplateURL* keyword_t_url_; 105 const string16 keyword_term_; 106 GURL keyword_url_; 107 108 MessageLoopForUI message_loop_; 109 content::TestBrowserThread ui_thread_; 110 content::TestBrowserThread io_thread_; 111 112 // URLFetcherFactory implementation registered. 113 net::TestURLFetcherFactory test_factory_; 114 115 // Profile we use. 116 TestingProfile profile_; 117 118 // The provider. 119 scoped_refptr<SearchProvider> provider_; 120 121 // If true, OnProviderUpdate exits out of the current message loop. 122 bool quit_when_done_; 123 124 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest); 125}; 126 127void SearchProviderTest::SetUp() { 128 SearchProvider::set_query_suggest_immediately(true); 129 130 // We need both the history service and template url model loaded. 131 profile_.CreateHistoryService(true, false); 132 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 133 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 134 135 TemplateURLService* turl_model = 136 TemplateURLServiceFactory::GetForProfile(&profile_); 137 138 turl_model->Load(); 139 140 // Reset the default TemplateURL. 141 TemplateURLData data; 142 data.short_name = ASCIIToUTF16("t"); 143 data.SetURL("http://defaultturl/{searchTerms}"); 144 data.suggestions_url = "http://defaultturl2/{searchTerms}"; 145 default_t_url_ = new TemplateURL(&profile_, data); 146 turl_model->Add(default_t_url_); 147 turl_model->SetDefaultSearchProvider(default_t_url_); 148 TemplateURLID default_provider_id = default_t_url_->id(); 149 ASSERT_NE(0, default_provider_id); 150 151 // Add url1, with search term term1_. 152 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1); 153 154 // Create another TemplateURL. 155 data.short_name = ASCIIToUTF16("k"); 156 data.SetKeyword(ASCIIToUTF16("k")); 157 data.SetURL("http://keyword/{searchTerms}"); 158 data.suggestions_url = "http://suggest_keyword/{searchTerms}"; 159 keyword_t_url_ = new TemplateURL(&profile_, data); 160 turl_model->Add(keyword_t_url_); 161 ASSERT_NE(0, keyword_t_url_->id()); 162 163 // Add a page and search term for keyword_t_url_. 164 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1); 165 166 // Keywords are updated by the InMemoryHistoryBackend only after the message 167 // has been processed on the history thread. Block until history processes all 168 // requests to ensure the InMemoryDatabase is the state we expect it. 169 profile_.BlockUntilHistoryProcessesPendingRequests(); 170 171 provider_ = new SearchProvider(this, &profile_); 172} 173 174void SearchProviderTest::OnProviderUpdate(bool updated_matches) { 175 if (quit_when_done_ && provider_->done()) { 176 quit_when_done_ = false; 177 message_loop_.Quit(); 178 } 179} 180 181void SearchProviderTest::RunTillProviderDone() { 182 if (provider_->done()) 183 return; 184 185 quit_when_done_ = true; 186#if defined(OS_ANDROID) 187 // Android doesn't have Run(), only Start(). 188 message_loop_.Start(); 189#else 190 base::RunLoop run_loop; 191 run_loop.Run(); 192#endif 193} 194 195void SearchProviderTest::QueryForInput(const string16& text, 196 const string16& desired_tld, 197 bool prevent_inline_autocomplete) { 198 // Start a query. 199 AutocompleteInput input(text, desired_tld, prevent_inline_autocomplete, 200 false, true, AutocompleteInput::ALL_MATCHES); 201 provider_->Start(input, false); 202 203 // RunAllPending so that the task scheduled by SearchProvider to create the 204 // URLFetchers runs. 205 message_loop_.RunAllPending(); 206} 207 208void SearchProviderTest::QueryForInputAndSetWYTMatch( 209 const string16& text, 210 AutocompleteMatch* wyt_match) { 211 QueryForInput(text, string16(), false); 212 profile_.BlockUntilHistoryProcessesPendingRequests(); 213 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 214 EXPECT_NE(InstantController::IsInstantEnabled(&profile_), provider_->done()); 215 if (!wyt_match) 216 return; 217 ASSERT_GE(provider_->matches().size(), 1u); 218 EXPECT_TRUE(FindMatchWithDestination(GURL( 219 default_t_url_->url_ref().ReplaceSearchTerms( 220 TemplateURLRef::SearchTermsArgs(text))), 221 wyt_match)); 222} 223 224void SearchProviderTest::TearDown() { 225 message_loop_.RunAllPending(); 226 227 // Shutdown the provider before the profile. 228 provider_ = NULL; 229} 230 231GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url, 232 string16 term, 233 int visit_count) { 234 HistoryService* history = 235 HistoryServiceFactory::GetForProfile(&profile_, 236 Profile::EXPLICIT_ACCESS); 237 GURL search(t_url->url_ref().ReplaceSearchTerms( 238 TemplateURLRef::SearchTermsArgs(term))); 239 static base::Time last_added_time; 240 last_added_time = std::max(base::Time::Now(), 241 last_added_time + base::TimeDelta::FromMicroseconds(1)); 242 history->AddPageWithDetails(search, string16(), visit_count, visit_count, 243 last_added_time, false, history::SOURCE_BROWSED); 244 history->SetKeywordSearchTermsForURL(search, t_url->id(), term); 245 return search; 246} 247 248bool SearchProviderTest::FindMatchWithContents(const string16& contents, 249 AutocompleteMatch* match) { 250 for (ACMatches::const_iterator i = provider_->matches().begin(); 251 i != provider_->matches().end(); ++i) { 252 if (i->contents == contents) { 253 *match = *i; 254 return true; 255 } 256 } 257 return false; 258} 259 260bool SearchProviderTest::FindMatchWithDestination(const GURL& url, 261 AutocompleteMatch* match) { 262 for (ACMatches::const_iterator i = provider_->matches().begin(); 263 i != provider_->matches().end(); ++i) { 264 if (i->destination_url == url) { 265 *match = *i; 266 return true; 267 } 268 } 269 return false; 270} 271 272void SearchProviderTest::FinishDefaultSuggestQuery() { 273 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 274 SearchProvider::kDefaultProviderURLFetcherID); 275 ASSERT_TRUE(default_fetcher); 276 277 // Tell the SearchProvider the default suggest query is done. 278 default_fetcher->set_response_code(200); 279 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 280} 281 282// Tests ----------------------------------------------------------------------- 283 284// Make sure we query history for the default provider and a URLFetcher is 285// created for the default provider suggest results. 286TEST_F(SearchProviderTest, QueryDefaultProvider) { 287 string16 term = term1_.substr(0, term1_.length() - 1); 288 QueryForInput(term, string16(), false); 289 290 // Make sure the default providers suggest service was queried. 291 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 292 SearchProvider::kDefaultProviderURLFetcherID); 293 ASSERT_TRUE(fetcher); 294 295 // And the URL matches what we expected. 296 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 297 TemplateURLRef::SearchTermsArgs(term))); 298 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url); 299 300 // Tell the SearchProvider the suggest query is done. 301 fetcher->set_response_code(200); 302 fetcher->delegate()->OnURLFetchComplete(fetcher); 303 fetcher = NULL; 304 305 // Run till the history results complete. 306 RunTillProviderDone(); 307 308 // The SearchProvider is done. Make sure it has a result for the history 309 // term term1. 310 AutocompleteMatch term1_match; 311 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match)); 312 // Term1 should not have a description, it's set later. 313 EXPECT_TRUE(term1_match.description.empty()); 314 315 AutocompleteMatch wyt_match; 316 EXPECT_TRUE(FindMatchWithDestination( 317 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 318 TemplateURLRef::SearchTermsArgs(term))), &wyt_match)); 319 EXPECT_TRUE(wyt_match.description.empty()); 320 321 // The match for term1 should be more relevant than the what you typed result. 322 EXPECT_GT(term1_match.relevance, wyt_match.relevance); 323} 324 325TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) { 326 string16 term = term1_.substr(0, term1_.length() - 1); 327 QueryForInput(term, string16(), true); 328 329 ASSERT_FALSE(provider_->matches().empty()); 330 ASSERT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 331 provider_->matches()[0].type); 332} 333 334// Issues a query that matches the registered keyword and makes sure history 335// is queried as well as URLFetchers getting created. 336TEST_F(SearchProviderTest, QueryKeywordProvider) { 337 string16 term = keyword_term_.substr(0, keyword_term_.length() - 1); 338 QueryForInput(keyword_t_url_->keyword() + UTF8ToUTF16(" ") + term, 339 string16(), false); 340 341 // Make sure the default providers suggest service was queried. 342 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 343 SearchProvider::kDefaultProviderURLFetcherID); 344 ASSERT_TRUE(default_fetcher); 345 346 // Tell the SearchProvider the default suggest query is done. 347 default_fetcher->set_response_code(200); 348 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 349 default_fetcher = NULL; 350 351 // Make sure the keyword providers suggest service was queried. 352 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID( 353 SearchProvider::kKeywordProviderURLFetcherID); 354 ASSERT_TRUE(keyword_fetcher); 355 356 // And the URL matches what we expected. 357 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms( 358 TemplateURLRef::SearchTermsArgs(term))); 359 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url); 360 361 // Tell the SearchProvider the keyword suggest query is done. 362 keyword_fetcher->set_response_code(200); 363 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 364 keyword_fetcher = NULL; 365 366 // Run till the history results complete. 367 RunTillProviderDone(); 368 369 // The SearchProvider is done. Make sure it has a result for the history 370 // term keyword. 371 AutocompleteMatch match; 372 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match)); 373 374 // The match should have an associated keyword. 375 EXPECT_FALSE(match.keyword.empty()); 376 377 // The fill into edit should contain the keyword. 378 EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_, 379 match.fill_into_edit); 380} 381 382TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) { 383 // None of the following input strings should be sent to the suggest server, 384 // because they may contain private data. 385 const char* inputs[] = { 386 "username:password", 387 "http://username:password", 388 "https://username:password", 389 "username:password@hostname", 390 "http://username:password@hostname/", 391 "file://filename", 392 "data://data", 393 "unknownscheme:anything", 394 "http://hostname/?query=q", 395 "http://hostname/path#ref", 396 "https://hostname/path", 397 }; 398 399 for (size_t i = 0; i < arraysize(inputs); ++i) { 400 QueryForInput(ASCIIToUTF16(inputs[i]), string16(), false); 401 // Make sure the default providers suggest service was not queried. 402 ASSERT_TRUE(test_factory_.GetFetcherByID( 403 SearchProvider::kDefaultProviderURLFetcherID) == NULL); 404 // Run till the history results complete. 405 RunTillProviderDone(); 406 } 407} 408 409// Make sure FinalizeInstantQuery works. 410TEST_F(SearchProviderTest, FinalizeInstantQuery) { 411 PrefService* service = profile_.GetPrefs(); 412 service->SetBoolean(prefs::kInstantEnabled, true); 413 414 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("foo"), 415 NULL)); 416 417 // Tell the provider instant is done. 418 provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), 419 InstantSuggestion(ASCIIToUTF16("bar"), 420 INSTANT_COMPLETE_NOW, 421 INSTANT_SUGGESTION_SEARCH)); 422 423 // The provider should now be done. 424 EXPECT_TRUE(provider_->done()); 425 426 // There should be two matches, one for what you typed, the other for 427 // 'foobar'. 428 EXPECT_EQ(2u, provider_->matches().size()); 429 GURL instant_url(default_t_url_->url_ref().ReplaceSearchTerms( 430 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("foobar")))); 431 AutocompleteMatch instant_match; 432 EXPECT_TRUE(FindMatchWithDestination(instant_url, &instant_match)); 433 434 // And the 'foobar' match should not have a description, it'll be set later. 435 EXPECT_TRUE(instant_match.description.empty()); 436 437 // Make sure the what you typed match has no description. 438 AutocompleteMatch wyt_match; 439 EXPECT_TRUE(FindMatchWithDestination( 440 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 441 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("foo")))), 442 &wyt_match)); 443 EXPECT_TRUE(wyt_match.description.empty()); 444 445 // The instant search should be more relevant. 446 EXPECT_GT(instant_match.relevance, wyt_match.relevance); 447} 448 449// Make sure that if FinalizeInstantQuery is invoked before suggest results 450// return, the suggest text from FinalizeInstantQuery is remembered. 451TEST_F(SearchProviderTest, RememberInstantQuery) { 452 PrefService* service = profile_.GetPrefs(); 453 service->SetBoolean(prefs::kInstantEnabled, true); 454 455 QueryForInput(ASCIIToUTF16("foo"), string16(), false); 456 457 // Finalize the instant query immediately. 458 provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), 459 InstantSuggestion(ASCIIToUTF16("bar"), 460 INSTANT_COMPLETE_NOW, 461 INSTANT_SUGGESTION_SEARCH)); 462 463 // There should be two matches, one for what you typed, the other for 464 // 'foobar'. 465 EXPECT_EQ(2u, provider_->matches().size()); 466 GURL instant_url(default_t_url_->url_ref().ReplaceSearchTerms( 467 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("foobar")))); 468 AutocompleteMatch instant_match; 469 EXPECT_TRUE(FindMatchWithDestination(instant_url, &instant_match)); 470 471 // Wait until history and the suggest query complete. 472 profile_.BlockUntilHistoryProcessesPendingRequests(); 473 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 474 475 // Provider should be done. 476 EXPECT_TRUE(provider_->done()); 477 478 // There should be two matches, one for what you typed, the other for 479 // 'foobar'. 480 EXPECT_EQ(2u, provider_->matches().size()); 481 EXPECT_TRUE(FindMatchWithDestination(instant_url, &instant_match)); 482 483 // And the 'foobar' match should not have a description, it'll be set later. 484 EXPECT_TRUE(instant_match.description.empty()); 485} 486 487// Make sure that if trailing whitespace is added to the text supplied to 488// AutocompleteInput the default suggest text is cleared. 489TEST_F(SearchProviderTest, DifferingText) { 490 PrefService* service = profile_.GetPrefs(); 491 service->SetBoolean(prefs::kInstantEnabled, true); 492 493 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("foo"), 494 NULL)); 495 496 // Finalize the instant query immediately. 497 provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), 498 InstantSuggestion(ASCIIToUTF16("bar"), 499 INSTANT_COMPLETE_NOW, 500 INSTANT_SUGGESTION_SEARCH)); 501 502 // Query with the same input text, but trailing whitespace. 503 AutocompleteMatch instant_match; 504 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("foo "), 505 &instant_match)); 506 507 // There should only one match, for what you typed. 508 EXPECT_EQ(1u, provider_->matches().size()); 509 EXPECT_FALSE(instant_match.destination_url.is_empty()); 510} 511 512TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) { 513 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( 514 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor); 515 GURL url = AddSearchToHistory(default_t_url_, 516 ASCIIToUTF16("docs.google.com"), 1); 517 518 // Add the term as a url. 519 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)-> 520 AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1, 521 base::Time::Now(), false, history::SOURCE_BROWSED); 522 profile_.BlockUntilHistoryProcessesPendingRequests(); 523 524 AutocompleteMatch wyt_match; 525 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"), 526 &wyt_match)); 527 528 // There should be two matches, one for what you typed, the other for 529 // 'docs.google.com'. The search term should have a lower priority than the 530 // what you typed match. 531 ASSERT_EQ(2u, provider_->matches().size()); 532 AutocompleteMatch term_match; 533 EXPECT_TRUE(FindMatchWithDestination(url, &term_match)); 534 EXPECT_GT(wyt_match.relevance, term_match.relevance); 535} 536 537// A multiword search with one visit should not autocomplete until multiple 538// words are typed. 539TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) { 540 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"), 541 1)); 542 profile_.BlockUntilHistoryProcessesPendingRequests(); 543 544 AutocompleteMatch wyt_match; 545 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"), 546 &wyt_match)); 547 ASSERT_EQ(2u, provider_->matches().size()); 548 AutocompleteMatch term_match; 549 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 550 EXPECT_GT(wyt_match.relevance, term_match.relevance); 551 552 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"), 553 &wyt_match)); 554 ASSERT_EQ(2u, provider_->matches().size()); 555 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 556 EXPECT_GT(term_match.relevance, wyt_match.relevance); 557} 558 559// A multiword search with more than one visit should autocomplete immediately. 560TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) { 561 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 562 2)); 563 profile_.BlockUntilHistoryProcessesPendingRequests(); 564 565 AutocompleteMatch wyt_match; 566 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"), 567 &wyt_match)); 568 ASSERT_EQ(2u, provider_->matches().size()); 569 AutocompleteMatch term_match; 570 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 571 EXPECT_GT(term_match.relevance, wyt_match.relevance); 572} 573 574// Autocompletion should work at a word boundary after a space. 575TEST_F(SearchProviderTest, AutocompleteAfterSpace) { 576 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 577 2)); 578 profile_.BlockUntilHistoryProcessesPendingRequests(); 579 580 AutocompleteMatch wyt_match; 581 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "), 582 &wyt_match)); 583 ASSERT_EQ(2u, provider_->matches().size()); 584 AutocompleteMatch term_match; 585 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 586 EXPECT_GT(term_match.relevance, wyt_match.relevance); 587} 588 589// Newer multiword searches should score more highly than older ones. 590TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) { 591 GURL term_url_a(AddSearchToHistory(default_t_url_, 592 ASCIIToUTF16("three searches aaa"), 1)); 593 GURL term_url_b(AddSearchToHistory(default_t_url_, 594 ASCIIToUTF16("three searches bbb"), 1)); 595 profile_.BlockUntilHistoryProcessesPendingRequests(); 596 597 AutocompleteMatch wyt_match; 598 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"), 599 &wyt_match)); 600 ASSERT_EQ(3u, provider_->matches().size()); 601 AutocompleteMatch term_match_a; 602 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 603 AutocompleteMatch term_match_b; 604 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 605 EXPECT_GT(term_match_b.relevance, term_match_a.relevance); 606 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 607} 608 609// An autocompleted multiword search should not be replaced by a different 610// autocompletion while the user is still typing a valid prefix. 611TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) { 612 GURL term_url_a(AddSearchToHistory(default_t_url_, 613 ASCIIToUTF16("four searches aaa"), 2)); 614 GURL term_url_b(AddSearchToHistory(default_t_url_, 615 ASCIIToUTF16("four searches bbb"), 1)); 616 profile_.BlockUntilHistoryProcessesPendingRequests(); 617 618 AutocompleteMatch wyt_match; 619 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"), 620 &wyt_match)); 621 ASSERT_EQ(3u, provider_->matches().size()); 622 AutocompleteMatch term_match_a; 623 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 624 AutocompleteMatch term_match_b; 625 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 626 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 627 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 628 629 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"), 630 &wyt_match)); 631 ASSERT_EQ(3u, provider_->matches().size()); 632 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 633 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 634 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 635 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 636} 637 638// Non-completable multiword searches should not crowd out single-word searches. 639TEST_F(SearchProviderTest, DontCrowdOutSingleWords) { 640 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1)); 641 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1); 642 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1); 643 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1); 644 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1); 645 profile_.BlockUntilHistoryProcessesPendingRequests(); 646 647 AutocompleteMatch wyt_match; 648 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"), 649 &wyt_match)); 650 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size()); 651 AutocompleteMatch term_match; 652 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 653 EXPECT_GT(term_match.relevance, wyt_match.relevance); 654} 655 656// Inline autocomplete matches regardless of case differences from the input. 657TEST_F(SearchProviderTest, InlineMixedCaseMatches) { 658 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1)); 659 profile_.BlockUntilHistoryProcessesPendingRequests(); 660 661 AutocompleteMatch wyt_match; 662 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"), 663 &wyt_match)); 664 ASSERT_EQ(2u, provider_->matches().size()); 665 AutocompleteMatch term_match; 666 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 667 EXPECT_GT(term_match.relevance, wyt_match.relevance); 668 EXPECT_EQ(1u, term_match.inline_autocomplete_offset); 669 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit); 670} 671 672// Verifies AutocompleteControllers sets descriptions for results correctly. 673TEST_F(SearchProviderTest, UpdateKeywordDescriptions) { 674 // Add an entry that corresponds to a keyword search with 'term2'. 675 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1); 676 profile_.BlockUntilHistoryProcessesPendingRequests(); 677 678 AutocompleteController controller(&profile_, NULL, 679 AutocompleteProvider::TYPE_SEARCH); 680 controller.Start(ASCIIToUTF16("k t"), string16(), false, false, true, 681 AutocompleteInput::ALL_MATCHES); 682 const AutocompleteResult& result = controller.result(); 683 684 // There should be two matches, one for the keyword one for what you typed. 685 ASSERT_EQ(2u, result.size()); 686 687 EXPECT_FALSE(result.match_at(0).keyword.empty()); 688 EXPECT_FALSE(result.match_at(1).keyword.empty()); 689 EXPECT_NE(result.match_at(0).keyword, result.match_at(1).keyword); 690 691 EXPECT_FALSE(result.match_at(0).description.empty()); 692 EXPECT_FALSE(result.match_at(1).description.empty()); 693 EXPECT_NE(result.match_at(0).description, result.match_at(1).description); 694} 695 696// Verifies Navsuggest results don't set a TemplateURL, which instant relies on. 697// Also verifies that just the *first* navigational result is listed as a match. 698TEST_F(SearchProviderTest, NavSuggest) { 699 QueryForInput(ASCIIToUTF16("a.c"), string16(), false); 700 701 // Make sure the default providers suggest service was queried. 702 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 703 SearchProvider::kDefaultProviderURLFetcherID); 704 ASSERT_TRUE(fetcher); 705 706 // Tell the SearchProvider the suggest query is done. 707 fetcher->set_response_code(200); 708 fetcher->SetResponseString( 709 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 710 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 711 fetcher->delegate()->OnURLFetchComplete(fetcher); 712 fetcher = NULL; 713 714 // Run till the history results complete. 715 RunTillProviderDone(); 716 717 // Make sure the only match is 'a.com' and it doesn't have a template_url. 718 AutocompleteMatch nav_match; 719 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 720 EXPECT_TRUE(nav_match.keyword.empty()); 721 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 722} 723 724// Verifies that the most relevant suggest results are added properly. 725TEST_F(SearchProviderTest, SuggestRelevance) { 726 QueryForInput(ASCIIToUTF16("a"), string16(), false); 727 728 // Make sure the default provider's suggest service was queried. 729 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 730 SearchProvider::kDefaultProviderURLFetcherID); 731 ASSERT_TRUE(fetcher); 732 733 // Tell the SearchProvider the suggest query is done. 734 fetcher->set_response_code(200); 735 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 736 fetcher->delegate()->OnURLFetchComplete(fetcher); 737 fetcher = NULL; 738 739 // Run till the history results complete. 740 RunTillProviderDone(); 741 742 // Check the expected verbatim and (first 3) suggestions' relative relevances. 743 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 744 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 745 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 746 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 747 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 748 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 749 EXPECT_GT(verbatim.relevance, match_a1.relevance); 750 EXPECT_GT(match_a1.relevance, match_a2.relevance); 751 EXPECT_GT(match_a2.relevance, match_a3.relevance); 752} 753 754// Verifies that suggest experiment results are added properly. 755TEST_F(SearchProviderTest, SuggestRelevanceExperiment) { 756 const std::string kNotApplicable("Not Applicable"); 757 struct { 758 const std::string json; 759 const std::string matches[4]; 760 } cases[] = { 761 // Ensure that suggestrelevance scores reorder matches. 762 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 763 { "a", "c", "b", kNotApplicable } }, 764 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 765 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 766 "\"google:suggestrelevance\":[1, 2]}]", 767 { "a", "c.com", kNotApplicable, kNotApplicable } }, 768 769 // Ensure that verbatimrelevance scores reorder or suppress what-you-typed. 770 // Negative values will have no effect; the calculated value will be used. 771 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 772 "\"google:suggestrelevance\":[9998]}]", 773 { "a", "a1", kNotApplicable, kNotApplicable } }, 774 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 775 "\"google:suggestrelevance\":[9999]}]", 776 { "a1", "a", kNotApplicable, kNotApplicable } }, 777 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 778 "\"google:suggestrelevance\":[9999]}]", 779 { "a1", kNotApplicable, kNotApplicable, kNotApplicable } }, 780 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 781 "\"google:suggestrelevance\":[9999]}]", 782 { "a1", "a", kNotApplicable, kNotApplicable } }, 783 { "[\"a\",[\"http://a.com\"],[],[]," 784 "{\"google:suggesttype\":[\"NAVIGATION\"]," 785 "\"google:verbatimrelevance\":9999," 786 "\"google:suggestrelevance\":[9998]}]", 787 { "a", "a.com", kNotApplicable, kNotApplicable } }, 788 { "[\"a\",[\"http://a.com\"],[],[]," 789 "{\"google:suggesttype\":[\"NAVIGATION\"]," 790 "\"google:verbatimrelevance\":9998," 791 "\"google:suggestrelevance\":[9999]}]", 792 { "a.com", "a", kNotApplicable, kNotApplicable } }, 793 { "[\"a\",[\"http://a.com\"],[],[]," 794 "{\"google:suggesttype\":[\"NAVIGATION\"]," 795 "\"google:verbatimrelevance\":0," 796 "\"google:suggestrelevance\":[9999]}]", 797 { "a.com", kNotApplicable, kNotApplicable, kNotApplicable } }, 798 { "[\"a\",[\"http://a.com\"],[],[]," 799 "{\"google:suggesttype\":[\"NAVIGATION\"]," 800 "\"google:verbatimrelevance\":-1," 801 "\"google:suggestrelevance\":[9999]}]", 802 { "a.com", "a", kNotApplicable, kNotApplicable } }, 803 804 // Ensure that both types of relevance scores reorder matches together. 805 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 806 "\"google:verbatimrelevance\":9998}]", 807 { "a1", "a", "a2", kNotApplicable } }, 808 809 // Ensure that only inlinable matches may be ranked as the highest result. 810 // Ignore all suggested relevance scores if this constraint is violated. 811 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 812 { "a", "b", kNotApplicable, kNotApplicable } }, 813 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 814 "\"google:verbatimrelevance\":0}]", 815 { "a", "b", kNotApplicable, kNotApplicable } }, 816 { "[\"a\",[\"http://b.com\"],[],[]," 817 "{\"google:suggesttype\":[\"NAVIGATION\"]," 818 "\"google:suggestrelevance\":[9999]}]", 819 { "a", "b.com", kNotApplicable, kNotApplicable } }, 820 { "[\"a\",[\"http://b.com\"],[],[]," 821 "{\"google:suggesttype\":[\"NAVIGATION\"]," 822 "\"google:suggestrelevance\":[9999]," 823 "\"google:verbatimrelevance\":0}]", 824 { "a", "b.com", kNotApplicable, kNotApplicable } }, 825 826 // Ensure that the top result is ranked as highly as calculated verbatim. 827 // Ignore the suggested verbatim relevance if this constraint is violated. 828 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 829 { "a", "a1", kNotApplicable, kNotApplicable } }, 830 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 831 { "a", "a1", kNotApplicable, kNotApplicable } }, 832 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 833 "\"google:verbatimrelevance\":0}]", 834 { "a", "a1", kNotApplicable, kNotApplicable } }, 835 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 836 "\"google:verbatimrelevance\":0}]", 837 { "a", "a2", "a1", kNotApplicable } }, 838 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 839 "\"google:verbatimrelevance\":2}]", 840 { "a", "a2", "a1", kNotApplicable } }, 841 { "[\"a\",[\"http://a.com\"],[],[]," 842 "{\"google:suggesttype\":[\"NAVIGATION\"]," 843 "\"google:suggestrelevance\":[1]," 844 "\"google:verbatimrelevance\":0}]", 845 { "a", "a.com", kNotApplicable, kNotApplicable } }, 846 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 847 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 848 "\"google:suggestrelevance\":[1, 2]," 849 "\"google:verbatimrelevance\":0}]", 850 { "a", "a2.com", kNotApplicable, kNotApplicable } }, 851 852 // Ensure that all suggestions are considered, regardless of order. 853 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 854 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 855 { "a", "h", "g", "f" } }, 856 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 857 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 858 "\"http://h.com\"],[],[]," 859 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 860 "\"NAVIGATION\", \"NAVIGATION\"," 861 "\"NAVIGATION\", \"NAVIGATION\"," 862 "\"NAVIGATION\"]," 863 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 864 { "a", "h.com", kNotApplicable, kNotApplicable } }, 865 866 // Ensure that incorrectly sized suggestion relevance lists are ignored. 867 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 868 { "a", "a1", "a2", kNotApplicable } }, 869 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 870 { "a", "a1", kNotApplicable, kNotApplicable } }, 871 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 872 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 873 "\"google:suggestrelevance\":[1]}]", 874 { "a", "a1.com", kNotApplicable, kNotApplicable } }, 875 { "[\"a\",[\"http://a1.com\"],[],[]," 876 "{\"google:suggesttype\":[\"NAVIGATION\"]," 877 "\"google:suggestrelevance\":[9999, 1]}]", 878 { "a", "a1.com", kNotApplicable, kNotApplicable } }, 879 880 // Ensure that all 'verbatim' results are merged with their maximum score. 881 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 882 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 883 { "a2", "a", "a1", kNotApplicable } }, 884 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 885 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 886 "\"google:verbatimrelevance\":0}]", 887 { "a2", "a", "a1", kNotApplicable } }, 888 889 // Ensure that verbatim is always generated without other suggestions. 890 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 891 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 892 { "a", kNotApplicable, kNotApplicable, kNotApplicable } }, 893 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 894 { "a", kNotApplicable, kNotApplicable, kNotApplicable } }, 895 }; 896 897 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 898 QueryForInput(ASCIIToUTF16("a"), string16(), false); 899 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 900 SearchProvider::kDefaultProviderURLFetcherID); 901 fetcher->set_response_code(200); 902 fetcher->SetResponseString(cases[i].json); 903 fetcher->delegate()->OnURLFetchComplete(fetcher); 904 RunTillProviderDone(); 905 906 const ACMatches& matches = provider_->matches(); 907 // The top match must inline and score as highly as calculated verbatim. 908 EXPECT_NE(string16::npos, matches[0].inline_autocomplete_offset); 909 EXPECT_GE(matches[0].relevance, 1300); 910 911 size_t j = 0; 912 // Ensure that the returned matches equal the expectations. 913 for (; j < matches.size(); ++j) 914 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents); 915 // Ensure that no expected matches are missing. 916 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 917 EXPECT_EQ(kNotApplicable, cases[i].matches[j]); 918 } 919} 920 921// Verifies suggest experiment behavior for URL input. 922TEST_F(SearchProviderTest, SuggestRelevanceExperimentUrlInput) { 923 const std::string kNotApplicable("Not Applicable"); 924 struct { 925 const std::string input; 926 const std::string json; 927 const std::string match_contents[4]; 928 const AutocompleteMatch::Type match_types[4]; 929 } cases[] = { 930 // Ensure topmost NAVIGATION matches are allowed for URL input. 931 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 932 "{\"google:suggesttype\":[\"NAVIGATION\"]," 933 "\"google:suggestrelevance\":[9999]}]", 934 { "a.com/a", "a.com", kNotApplicable, kNotApplicable }, 935 { AutocompleteMatch::NAVSUGGEST, AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 936 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 937 938 // Ensure topmost SUGGEST matches are not allowed for URL input. 939 // SearchProvider disregards search and verbatim suggested relevances. 940 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 941 "{\"google:suggestrelevance\":[9999]}]", 942 { "a.com", "a.com info", kNotApplicable, kNotApplicable }, 943 { AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 944 AutocompleteMatch::SEARCH_SUGGEST, 945 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 946 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[]," 947 "{\"google:suggestrelevance\":[9999]}]", 948 { "a.com", "a.com/a", kNotApplicable, kNotApplicable }, 949 { AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 950 AutocompleteMatch::SEARCH_SUGGEST, 951 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 952 953 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 954 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 955 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 956 "\"google:suggestrelevance\":[9999, 9998]}]", 957 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 958 { AutocompleteMatch::NAVSUGGEST, 959 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 960 AutocompleteMatch::SEARCH_SUGGEST, 961 AutocompleteMatch::NUM_TYPES } }, 962 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 963 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 964 "\"google:suggestrelevance\":[9998, 9997]," 965 "\"google:verbatimrelevance\":9999}]", 966 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 967 { AutocompleteMatch::NAVSUGGEST, 968 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 969 AutocompleteMatch::SEARCH_SUGGEST, 970 AutocompleteMatch::NUM_TYPES } }, 971 972 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches. 973 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 974 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 975 "\"google:suggestrelevance\":[9999, 9998]}]", 976 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 977 { AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 978 AutocompleteMatch::NAVSUGGEST, 979 AutocompleteMatch::SEARCH_SUGGEST, 980 AutocompleteMatch::NUM_TYPES } }, 981 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 982 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 983 "\"google:suggestrelevance\":[9998, 9997]," 984 "\"google:verbatimrelevance\":9999}]", 985 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 986 { AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 987 AutocompleteMatch::NAVSUGGEST, 988 AutocompleteMatch::SEARCH_SUGGEST, 989 AutocompleteMatch::NUM_TYPES } }, 990}; 991 992 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 993 QueryForInput(ASCIIToUTF16(cases[i].input), string16(), false); 994 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 995 SearchProvider::kDefaultProviderURLFetcherID); 996 fetcher->set_response_code(200); 997 fetcher->SetResponseString(cases[i].json); 998 fetcher->delegate()->OnURLFetchComplete(fetcher); 999 RunTillProviderDone(); 1000 1001 size_t j = 0; 1002 const ACMatches& matches = provider_->matches(); 1003 // Ensure that the returned matches equal the expectations. 1004 for (; j < matches.size(); ++j) { 1005 EXPECT_EQ(ASCIIToUTF16(cases[i].match_contents[j]), matches[j].contents); 1006 EXPECT_EQ(cases[i].match_types[j], matches[j].type); 1007 } 1008 // Ensure that no expected matches are missing. 1009 for (; j < ARRAYSIZE_UNSAFE(cases[i].match_contents); ++j) { 1010 EXPECT_EQ(kNotApplicable, cases[i].match_contents[j]); 1011 EXPECT_EQ(AutocompleteMatch::NUM_TYPES, cases[i].match_types[j]); 1012 } 1013 } 1014} 1015 1016// Verifies suggest experiment behavior for REQUESTED_URL input w/|desired_tld|. 1017TEST_F(SearchProviderTest, SuggestRelevanceExperimentRequestedUrlInput) { 1018 const std::string kNotApplicable("Not Applicable"); 1019 struct { 1020 const std::string input; 1021 const std::string json; 1022 const std::string match_contents[4]; 1023 const AutocompleteMatch::Type match_types[4]; 1024 } cases[] = { 1025 // Ensure topmost NAVIGATION matches are allowed for REQUESTED_URL input. 1026 { "a", "[\"a\",[\"http://a.com/a\"],[],[]," 1027 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1028 "\"google:suggestrelevance\":[9999]}]", 1029 { "a.com/a", "a", kNotApplicable, kNotApplicable }, 1030 { AutocompleteMatch::NAVSUGGEST, AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1031 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 1032 1033 // Disallow topmost verbatim[-like] SUGGEST matches for REQUESTED_URL input. 1034 // To prevent this, SearchProvider generates a URL_WHAT_YOU_TYPED match. 1035 { "a", "[\"a\",[\"a\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1036 { "www.a.com", "a", kNotApplicable, kNotApplicable }, 1037 { AutocompleteMatch::URL_WHAT_YOU_TYPED, 1038 AutocompleteMatch::SEARCH_SUGGEST, 1039 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 1040 { "a", "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999}]", 1041 { "www.a.com", "a", "a1", kNotApplicable }, 1042 { AutocompleteMatch::URL_WHAT_YOU_TYPED, 1043 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1044 AutocompleteMatch::SEARCH_SUGGEST, AutocompleteMatch::NUM_TYPES } }, 1045 1046 // Allow topmost non-verbatim-like SUGGEST matches for REQUESTED_URL input. 1047 // This is needed so that (CTRL+A/C/etc.) doesn't alter inline text. 1048 { "a", "[\"a\",[\"a.com/a\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1049 { "a.com/a", "a", kNotApplicable, kNotApplicable }, 1050 { AutocompleteMatch::SEARCH_SUGGEST, 1051 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1052 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 1053 }; 1054 1055 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1056 QueryForInput(ASCIIToUTF16(cases[i].input), ASCIIToUTF16("com"), false); 1057 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1058 SearchProvider::kDefaultProviderURLFetcherID); 1059 fetcher->set_response_code(200); 1060 fetcher->SetResponseString(cases[i].json); 1061 fetcher->delegate()->OnURLFetchComplete(fetcher); 1062 RunTillProviderDone(); 1063 1064 size_t j = 0; 1065 const ACMatches& matches = provider_->matches(); 1066 // Ensure that the returned matches equal the expectations. 1067 for (; j < matches.size(); ++j) { 1068 EXPECT_EQ(ASCIIToUTF16(cases[i].match_contents[j]), matches[j].contents); 1069 EXPECT_EQ(cases[i].match_types[j], matches[j].type); 1070 } 1071 // Ensure that no expected matches are missing. 1072 for (; j < ARRAYSIZE_UNSAFE(cases[i].match_contents); ++j) { 1073 EXPECT_EQ(kNotApplicable, cases[i].match_contents[j]); 1074 EXPECT_EQ(AutocompleteMatch::NUM_TYPES, cases[i].match_types[j]); 1075 } 1076 } 1077} 1078 1079// Verifies inline autocompletion of navigational results. 1080TEST_F(SearchProviderTest, NavigationInline) { 1081 struct { 1082 const std::string input; 1083 const std::string url; 1084 // Test the expected fill_into_edit, which may drop "http://". 1085 // Some cases do not trim "http://" to match from the start of the scheme. 1086 const std::string fill_into_edit; 1087 size_t inline_offset; 1088 } cases[] = { 1089 // Do not inline matches that do not contain the input; trim http as needed. 1090 { "x", "http://www.abc.com", 1091 "www.abc.com", string16::npos }, 1092 { "https:", "http://www.abc.com", 1093 "www.abc.com", string16::npos }, 1094 { "abc.com/", "http://www.abc.com", 1095 "www.abc.com", string16::npos }, 1096 { "http://www.abc.com/a", "http://www.abc.com", 1097 "http://www.abc.com", string16::npos }, 1098 { "http://www.abc.com", "https://www.abc.com", 1099 "https://www.abc.com", string16::npos }, 1100 { "http://abc.com", "ftp://abc.com", 1101 "ftp://abc.com", string16::npos }, 1102 { "https://www.abc.com", "http://www.abc.com", 1103 "www.abc.com", string16::npos }, 1104 { "ftp://abc.com", "http://abc.com", 1105 "abc.com", string16::npos }, 1106 1107 // Do not inline matches with invalid input prefixes; trim http as needed. 1108 { "ttp", "http://www.abc.com", 1109 "www.abc.com", string16::npos }, 1110 { "://w", "http://www.abc.com", 1111 "www.abc.com", string16::npos }, 1112 { "ww.", "http://www.abc.com", 1113 "www.abc.com", string16::npos }, 1114 { ".ab", "http://www.abc.com", 1115 "www.abc.com", string16::npos }, 1116 { "bc", "http://www.abc.com", 1117 "www.abc.com", string16::npos }, 1118 { ".com", "http://www.abc.com", 1119 "www.abc.com", string16::npos }, 1120 1121 // Do not inline matches that omit input domain labels; trim http as needed. 1122 { "www.a", "http://a.com", 1123 "a.com", string16::npos }, 1124 { "http://www.a", "http://a.com", 1125 "http://a.com", string16::npos }, 1126 { "www.a", "ftp://a.com", 1127 "ftp://a.com", string16::npos }, 1128 { "ftp://www.a", "ftp://a.com", 1129 "ftp://a.com", string16::npos }, 1130 1131 // Input matching but with nothing to inline will not yield an offset. 1132 { "abc.com", "http://www.abc.com", 1133 "www.abc.com", string16::npos }, 1134 { "http://www.abc.com", "http://www.abc.com", 1135 "http://www.abc.com", string16::npos }, 1136 1137 // Inline matches when the input is a leading substring of the scheme. 1138 { "h", "http://www.abc.com", 1139 "http://www.abc.com", 1 }, 1140 { "http", "http://www.abc.com", 1141 "http://www.abc.com", 4 }, 1142 1143 // Inline matches when the input is a leading substring of the full URL. 1144 { "http:", "http://www.abc.com", 1145 "http://www.abc.com", 5 }, 1146 { "http://w", "http://www.abc.com", 1147 "http://www.abc.com", 8 }, 1148 { "http://www.", "http://www.abc.com", 1149 "http://www.abc.com", 11 }, 1150 { "http://www.ab", "http://www.abc.com", 1151 "http://www.abc.com", 13 }, 1152 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1153 "http://www.abc.com/path/file.htm?q=x#foo", 20 }, 1154 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1155 "http://abc.com/path/file.htm?q=x#foo", 16 }, 1156 1157 // Inline matches with valid URLPrefixes; only trim "http://". 1158 { "w", "http://www.abc.com", 1159 "www.abc.com", 1 }, 1160 { "www.a", "http://www.abc.com", 1161 "www.abc.com", 5 }, 1162 { "abc", "http://www.abc.com", 1163 "www.abc.com", 7 }, 1164 { "abc.c", "http://www.abc.com", 1165 "www.abc.com", 9 }, 1166 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1167 "www.abc.com/path/file.htm?q=x#foo", 13 }, 1168 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1169 "abc.com/path/file.htm?q=x#foo", 9 }, 1170 1171 // Inline matches using the maximal URLPrefix components. 1172 { "h", "http://help.com", 1173 "help.com", 1 }, 1174 { "http", "http://http.com", 1175 "http.com", 4 }, 1176 { "h", "http://www.help.com", 1177 "www.help.com", 5 }, 1178 { "http", "http://www.http.com", 1179 "www.http.com", 8 }, 1180 { "w", "http://www.www.com", 1181 "www.www.com", 5 }, 1182 1183 // Test similar behavior for the ftp and https schemes. 1184 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1185 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1186 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1187 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1188 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1189 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1190 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 1191 "ftp://abc.com/path/file.htm?q=x#foo", 8 }, 1192 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1193 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1194 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1195 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1196 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 1197 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1198 { "ab", "https://abc.com/path/file.htm?q=x#foo", 1199 "https://abc.com/path/file.htm?q=x#foo", 10 }, 1200 1201 // Forced query input should inline and retain the "?" prefix. 1202 { "?http://www.ab", "http://www.abc.com", 1203 "?http://www.abc.com", 14 }, 1204 { "?www.ab", "http://www.abc.com", 1205 "?www.abc.com", 7 }, 1206 { "?ab", "http://www.abc.com", 1207 "?www.abc.com", 7 }, 1208 }; 1209 1210 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1211 QueryForInput(ASCIIToUTF16(cases[i].input), string16(), false); 1212 SearchProvider::NavigationResult result(GURL(cases[i].url), string16(), 0); 1213 AutocompleteMatch match(provider_->NavigationToMatch(result, false)); 1214 EXPECT_EQ(cases[i].inline_offset, match.inline_autocomplete_offset); 1215 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 1216 } 1217} 1218 1219// Verifies that "http://" is not trimmed for input that is a leading substring. 1220TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 1221 const string16 input(ASCIIToUTF16("ht")); 1222 const string16 url(ASCIIToUTF16("http://a.com")); 1223 const SearchProvider::NavigationResult result(GURL(url), string16(), 0); 1224 1225 // Check the offset and strings when inline autocompletion is allowed. 1226 QueryForInput(input, string16(), false); 1227 AutocompleteMatch match_inline(provider_->NavigationToMatch(result, false)); 1228 EXPECT_EQ(2U, match_inline.inline_autocomplete_offset); 1229 EXPECT_EQ(url, match_inline.fill_into_edit); 1230 EXPECT_EQ(url, match_inline.contents); 1231 1232 // Check the same offset and strings when inline autocompletion is prevented. 1233 QueryForInput(input, string16(), true); 1234 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result, false)); 1235 EXPECT_EQ(string16::npos, match_prevent.inline_autocomplete_offset); 1236 EXPECT_EQ(url, match_prevent.fill_into_edit); 1237 EXPECT_EQ(url, match_prevent.contents); 1238} 1239 1240// Verifies that input "w" marks a more significant domain label than "www.". 1241TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 1242 QueryForInput(ASCIIToUTF16("w"), string16(), false); 1243 const GURL url("http://www.wow.com"); 1244 const SearchProvider::NavigationResult result(url, string16(), 0); 1245 AutocompleteMatch match(provider_->NavigationToMatch(result, false)); 1246 EXPECT_EQ(5U, match.inline_autocomplete_offset); 1247 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 1248 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 1249 1250 // Ensure that the match for input "w" is marked on "wow" and not "www". 1251 ASSERT_EQ(3U, match.contents_class.size()); 1252 EXPECT_EQ(0U, match.contents_class[0].offset); 1253 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1254 match.contents_class[0].style); 1255 EXPECT_EQ(4U, match.contents_class[1].offset); 1256 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 1257 AutocompleteMatch::ACMatchClassification::MATCH, 1258 match.contents_class[1].style); 1259 EXPECT_EQ(5U, match.contents_class[2].offset); 1260 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1261 match.contents_class[2].style); 1262} 1263