search_provider_unittest.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright 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/metrics/field_trial.h" 8#include "base/prefs/pref_service.h" 9#include "base/run_loop.h" 10#include "base/string_util.h" 11#include "base/time.h" 12#include "base/utf_string_conversions.h" 13#include "build/build_config.h" 14#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 15#include "chrome/browser/autocomplete/autocomplete_controller.h" 16#include "chrome/browser/autocomplete/autocomplete_input.h" 17#include "chrome/browser/autocomplete/autocomplete_match.h" 18#include "chrome/browser/autocomplete/autocomplete_provider.h" 19#include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 20#include "chrome/browser/autocomplete/history_url_provider.h" 21#include "chrome/browser/history/history_service.h" 22#include "chrome/browser/history/history_service_factory.h" 23#include "chrome/browser/omnibox/omnibox_field_trial.h" 24#include "chrome/browser/search/search.h" 25#include "chrome/browser/search_engines/template_url.h" 26#include "chrome/browser/search_engines/template_url_service.h" 27#include "chrome/browser/search_engines/template_url_service_factory.h" 28#include "chrome/common/metrics/entropy_provider.h" 29#include "chrome/common/pref_names.h" 30#include "chrome/test/base/testing_browser_process.h" 31#include "chrome/test/base/testing_profile.h" 32#include "content/public/test/test_browser_thread.h" 33#include "net/url_request/test_url_fetcher_factory.h" 34#include "net/url_request/url_request_status.h" 35#include "testing/gtest/include/gtest/gtest.h" 36 37using content::BrowserThread; 38 39// The following environment is configured for these tests: 40// . The TemplateURL default_t_url_ is set as the default provider. 41// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This 42// TemplateURL has a valid suggest and search URL. 43// . The URL created by using the search term term1_ with default_t_url_ is 44// added to history. 45// . The URL created by using the search term keyword_term_ with keyword_t_url_ 46// is added to history. 47// . test_factory_ is set as the URLFetcherFactory. 48class SearchProviderTest : public testing::Test, 49 public AutocompleteProviderListener { 50 public: 51 SearchProviderTest() 52 : default_t_url_(NULL), 53 term1_(UTF8ToUTF16("term1")), 54 keyword_t_url_(NULL), 55 keyword_term_(UTF8ToUTF16("keyword")), 56 ui_thread_(BrowserThread::UI, &message_loop_), 57 io_thread_(BrowserThread::IO), 58 quit_when_done_(false) { 59 io_thread_.Start(); 60 } 61 62 static void SetUpTestCase(); 63 64 static void TearDownTestCase(); 65 66 // See description above class for what this registers. 67 virtual void SetUp(); 68 69 virtual void TearDown(); 70 71 struct ResultInfo { 72 ResultInfo() : result_type(AutocompleteMatch::NUM_TYPES) { 73 } 74 ResultInfo(GURL gurl, 75 AutocompleteMatch::Type result_type, 76 string16 fill_into_edit) 77 : gurl(gurl), 78 result_type(result_type), 79 fill_into_edit(fill_into_edit) { 80 } 81 const GURL gurl; 82 const AutocompleteMatch::Type result_type; 83 const string16 fill_into_edit; 84 }; 85 struct TestData { 86 const string16 input; 87 const size_t num_results; 88 const ResultInfo output[3]; 89 }; 90 91 void RunTest(TestData* cases, int num_cases, bool prefer_keyword); 92 93 protected: 94 // Adds a search for |term|, using the engine |t_url| to the history, and 95 // returns the URL for that search. 96 GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count); 97 98 // Looks for a match in |provider_| with |contents| equal to |contents|. 99 // Sets |match| to it if found. Returns whether |match| was set. 100 bool FindMatchWithContents(const string16& contents, 101 AutocompleteMatch* match); 102 103 // Looks for a match in |provider_| with destination |url|. Sets |match| to 104 // it if found. Returns whether |match| was set. 105 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match); 106 107 // AutocompleteProviderListener: 108 // If we're waiting for the provider to finish, this exits the message loop. 109 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 110 111 // Waits until the provider instantiates a URLFetcher and returns it. 112 net::TestURLFetcher* WaitUntilURLFetcherIsReady(int fetcher_id); 113 114 // Runs a nested message loop until provider_ is done. The message loop is 115 // exited by way of OnProviderUpdate. 116 void RunTillProviderDone(); 117 118 // Invokes Start on provider_, then runs all pending tasks. 119 void QueryForInput(const string16& text, 120 bool prevent_inline_autocomplete); 121 122 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is 123 // non-NULL, sets it to the "what you typed" entry for |text|. 124 void QueryForInputAndSetWYTMatch(const string16& text, 125 AutocompleteMatch* wyt_match); 126 127 // Notifies the URLFetcher for the suggest query corresponding to the default 128 // search provider that it's done. 129 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE. 130 void FinishDefaultSuggestQuery(); 131 132 // See description above class for details of these fields. 133 TemplateURL* default_t_url_; 134 const string16 term1_; 135 GURL term1_url_; 136 TemplateURL* keyword_t_url_; 137 const string16 keyword_term_; 138 GURL keyword_url_; 139 140 MessageLoopForUI message_loop_; 141 content::TestBrowserThread ui_thread_; 142 content::TestBrowserThread io_thread_; 143 144 // URLFetcherFactory implementation registered. 145 net::TestURLFetcherFactory test_factory_; 146 147 // Profile we use. 148 TestingProfile profile_; 149 150 // The provider. 151 scoped_refptr<SearchProvider> provider_; 152 153 // If true, OnProviderUpdate exits out of the current message loop. 154 bool quit_when_done_; 155 156 // Needed for AutucompleteFieldTrial::ActivateStaticTrials(); 157 static base::FieldTrialList* field_trial_list_; 158 159 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest); 160}; 161 162// static 163base::FieldTrialList* SearchProviderTest::field_trial_list_ = NULL; 164 165// static 166void SearchProviderTest::SetUpTestCase() { 167 // Set up Suggest experiments. 168 field_trial_list_ = new base::FieldTrialList( 169 new metrics::SHA1EntropyProvider("foo")); 170 OmniboxFieldTrial::ActivateStaticTrials(); 171} 172 173// static 174void SearchProviderTest::TearDownTestCase() { 175 // Make sure the global instance of FieldTrialList is gone. 176 delete field_trial_list_; 177} 178 179void SearchProviderTest::SetUp() { 180 // Make sure that fetchers are automatically ungregistered upon destruction. 181 test_factory_.set_remove_fetcher_on_delete(true); 182 183 // We need both the history service and template url model loaded. 184 profile_.CreateHistoryService(true, false); 185 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 186 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 187 188 TemplateURLService* turl_model = 189 TemplateURLServiceFactory::GetForProfile(&profile_); 190 191 turl_model->Load(); 192 193 // Reset the default TemplateURL. 194 TemplateURLData data; 195 data.short_name = ASCIIToUTF16("t"); 196 data.SetURL("http://defaultturl/{searchTerms}"); 197 data.suggestions_url = "http://defaultturl2/{searchTerms}"; 198 data.instant_url = "http://does/not/exist"; 199 default_t_url_ = new TemplateURL(&profile_, data); 200 turl_model->Add(default_t_url_); 201 turl_model->SetDefaultSearchProvider(default_t_url_); 202 TemplateURLID default_provider_id = default_t_url_->id(); 203 ASSERT_NE(0, default_provider_id); 204 205 // Add url1, with search term term1_. 206 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1); 207 208 // Create another TemplateURL. 209 data.short_name = ASCIIToUTF16("k"); 210 data.SetKeyword(ASCIIToUTF16("k")); 211 data.SetURL("http://keyword/{searchTerms}"); 212 data.suggestions_url = "http://suggest_keyword/{searchTerms}"; 213 keyword_t_url_ = new TemplateURL(&profile_, data); 214 turl_model->Add(keyword_t_url_); 215 ASSERT_NE(0, keyword_t_url_->id()); 216 217 // Add a page and search term for keyword_t_url_. 218 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1); 219 220 // Keywords are updated by the InMemoryHistoryBackend only after the message 221 // has been processed on the history thread. Block until history processes all 222 // requests to ensure the InMemoryDatabase is the state we expect it. 223 profile_.BlockUntilHistoryProcessesPendingRequests(); 224 225 provider_ = new SearchProvider(this, &profile_); 226} 227 228void SearchProviderTest::OnProviderUpdate(bool updated_matches) { 229 if (quit_when_done_ && provider_->done()) { 230 quit_when_done_ = false; 231 message_loop_.Quit(); 232 } 233} 234 235net::TestURLFetcher* SearchProviderTest::WaitUntilURLFetcherIsReady( 236 int fetcher_id) { 237 net::TestURLFetcher* url_fetcher = test_factory_.GetFetcherByID(fetcher_id); 238 for (; !url_fetcher; url_fetcher = test_factory_.GetFetcherByID(fetcher_id)) 239 message_loop_.RunUntilIdle(); 240 return url_fetcher; 241} 242 243void SearchProviderTest::RunTillProviderDone() { 244 if (provider_->done()) 245 return; 246 247 quit_when_done_ = true; 248#if defined(OS_ANDROID) 249 // Android doesn't have Run(), only Start(). 250 message_loop_.Start(); 251#else 252 base::RunLoop run_loop; 253 run_loop.Run(); 254#endif 255} 256 257void SearchProviderTest::QueryForInput(const string16& text, 258 bool prevent_inline_autocomplete) { 259 // Start a query. 260 AutocompleteInput input(text, string16::npos, string16(), GURL(), 261 prevent_inline_autocomplete, 262 false, true, AutocompleteInput::ALL_MATCHES); 263 provider_->Start(input, false); 264 265 // RunUntilIdle so that the task scheduled by SearchProvider to create the 266 // URLFetchers runs. 267 message_loop_.RunUntilIdle(); 268} 269 270void SearchProviderTest::QueryForInputAndSetWYTMatch( 271 const string16& text, 272 AutocompleteMatch* wyt_match) { 273 QueryForInput(text, false); 274 profile_.BlockUntilHistoryProcessesPendingRequests(); 275 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 276 EXPECT_NE(chrome::search::IsInstantEnabled(&profile_), provider_->done()); 277 if (!wyt_match) 278 return; 279 ASSERT_GE(provider_->matches().size(), 1u); 280 EXPECT_TRUE(FindMatchWithDestination(GURL( 281 default_t_url_->url_ref().ReplaceSearchTerms( 282 TemplateURLRef::SearchTermsArgs(text))), 283 wyt_match)); 284} 285 286void SearchProviderTest::TearDown() { 287 message_loop_.RunUntilIdle(); 288 289 // Shutdown the provider before the profile. 290 provider_ = NULL; 291} 292 293void SearchProviderTest::RunTest(TestData* cases, 294 int num_cases, 295 bool prefer_keyword) { 296 ACMatches matches; 297 for (int i = 0; i < num_cases; ++i) { 298 AutocompleteInput input(cases[i].input, string16::npos, string16(), GURL(), 299 false, prefer_keyword, true, 300 AutocompleteInput::ALL_MATCHES); 301 provider_->Start(input, false); 302 matches = provider_->matches(); 303 string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input + 304 ASCIIToUTF16("; prefer_keyword was: ") + 305 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")); 306 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details; 307 if (matches.size() == cases[i].num_results) { 308 for (size_t j = 0; j < cases[i].num_results; ++j) { 309 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) << 310 diagnostic_details; 311 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) << 312 diagnostic_details; 313 EXPECT_EQ(cases[i].output[j].fill_into_edit, 314 matches[j].fill_into_edit) << 315 diagnostic_details; 316 } 317 } 318 } 319} 320 321GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url, 322 string16 term, 323 int visit_count) { 324 HistoryService* history = 325 HistoryServiceFactory::GetForProfile(&profile_, 326 Profile::EXPLICIT_ACCESS); 327 GURL search(t_url->url_ref().ReplaceSearchTerms( 328 TemplateURLRef::SearchTermsArgs(term))); 329 static base::Time last_added_time; 330 last_added_time = std::max(base::Time::Now(), 331 last_added_time + base::TimeDelta::FromMicroseconds(1)); 332 history->AddPageWithDetails(search, string16(), visit_count, visit_count, 333 last_added_time, false, history::SOURCE_BROWSED); 334 history->SetKeywordSearchTermsForURL(search, t_url->id(), term); 335 return search; 336} 337 338bool SearchProviderTest::FindMatchWithContents(const string16& contents, 339 AutocompleteMatch* match) { 340 for (ACMatches::const_iterator i = provider_->matches().begin(); 341 i != provider_->matches().end(); ++i) { 342 if (i->contents == contents) { 343 *match = *i; 344 return true; 345 } 346 } 347 return false; 348} 349 350bool SearchProviderTest::FindMatchWithDestination(const GURL& url, 351 AutocompleteMatch* match) { 352 for (ACMatches::const_iterator i = provider_->matches().begin(); 353 i != provider_->matches().end(); ++i) { 354 if (i->destination_url == url) { 355 *match = *i; 356 return true; 357 } 358 } 359 return false; 360} 361 362void SearchProviderTest::FinishDefaultSuggestQuery() { 363 net::TestURLFetcher* default_fetcher = WaitUntilURLFetcherIsReady( 364 SearchProvider::kDefaultProviderURLFetcherID); 365 ASSERT_TRUE(default_fetcher); 366 367 // Tell the SearchProvider the default suggest query is done. 368 default_fetcher->set_response_code(200); 369 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 370} 371 372// Tests ----------------------------------------------------------------------- 373 374// Make sure we query history for the default provider and a URLFetcher is 375// created for the default provider suggest results. 376TEST_F(SearchProviderTest, QueryDefaultProvider) { 377 string16 term = term1_.substr(0, term1_.length() - 1); 378 QueryForInput(term, false); 379 380 // Make sure the default providers suggest service was queried. 381 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 382 SearchProvider::kDefaultProviderURLFetcherID); 383 ASSERT_TRUE(fetcher); 384 385 // And the URL matches what we expected. 386 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 387 TemplateURLRef::SearchTermsArgs(term))); 388 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url); 389 390 // Tell the SearchProvider the suggest query is done. 391 fetcher->set_response_code(200); 392 fetcher->delegate()->OnURLFetchComplete(fetcher); 393 fetcher = NULL; 394 395 // Run till the history results complete. 396 RunTillProviderDone(); 397 398 // The SearchProvider is done. Make sure it has a result for the history 399 // term term1. 400 AutocompleteMatch term1_match; 401 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match)); 402 // Term1 should not have a description, it's set later. 403 EXPECT_TRUE(term1_match.description.empty()); 404 405 AutocompleteMatch wyt_match; 406 EXPECT_TRUE(FindMatchWithDestination( 407 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 408 TemplateURLRef::SearchTermsArgs(term))), &wyt_match)); 409 EXPECT_TRUE(wyt_match.description.empty()); 410 411 // The match for term1 should be more relevant than the what you typed result. 412 EXPECT_GT(term1_match.relevance, wyt_match.relevance); 413} 414 415TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) { 416 string16 term = term1_.substr(0, term1_.length() - 1); 417 QueryForInput(term, true); 418 419 ASSERT_FALSE(provider_->matches().empty()); 420 ASSERT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 421 provider_->matches()[0].type); 422} 423 424// Issues a query that matches the registered keyword and makes sure history 425// is queried as well as URLFetchers getting created. 426TEST_F(SearchProviderTest, QueryKeywordProvider) { 427 string16 term = keyword_term_.substr(0, keyword_term_.length() - 1); 428 QueryForInput(keyword_t_url_->keyword() + UTF8ToUTF16(" ") + term, false); 429 430 // Make sure the default providers suggest service was queried. 431 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 432 SearchProvider::kDefaultProviderURLFetcherID); 433 ASSERT_TRUE(default_fetcher); 434 435 // Tell the SearchProvider the default suggest query is done. 436 default_fetcher->set_response_code(200); 437 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 438 default_fetcher = NULL; 439 440 // Make sure the keyword providers suggest service was queried. 441 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID( 442 SearchProvider::kKeywordProviderURLFetcherID); 443 ASSERT_TRUE(keyword_fetcher); 444 445 // And the URL matches what we expected. 446 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms( 447 TemplateURLRef::SearchTermsArgs(term))); 448 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url); 449 450 // Tell the SearchProvider the keyword suggest query is done. 451 keyword_fetcher->set_response_code(200); 452 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 453 keyword_fetcher = NULL; 454 455 // Run till the history results complete. 456 RunTillProviderDone(); 457 458 // The SearchProvider is done. Make sure it has a result for the history 459 // term keyword. 460 AutocompleteMatch match; 461 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match)); 462 463 // The match should have an associated keyword. 464 EXPECT_FALSE(match.keyword.empty()); 465 466 // The fill into edit should contain the keyword. 467 EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_, 468 match.fill_into_edit); 469} 470 471TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) { 472 // None of the following input strings should be sent to the suggest server, 473 // because they may contain private data. 474 const char* inputs[] = { 475 "username:password", 476 "http://username:password", 477 "https://username:password", 478 "username:password@hostname", 479 "http://username:password@hostname/", 480 "file://filename", 481 "data://data", 482 "unknownscheme:anything", 483 "http://hostname/?query=q", 484 "http://hostname/path#ref", 485 "https://hostname/path", 486 }; 487 488 for (size_t i = 0; i < arraysize(inputs); ++i) { 489 QueryForInput(ASCIIToUTF16(inputs[i]), false); 490 // Make sure the default providers suggest service was not queried. 491 ASSERT_TRUE(test_factory_.GetFetcherByID( 492 SearchProvider::kDefaultProviderURLFetcherID) == NULL); 493 // Run till the history results complete. 494 RunTillProviderDone(); 495 } 496} 497 498// Make sure FinalizeInstantQuery works. 499TEST_F(SearchProviderTest, FinalizeInstantQuery) { 500 PrefService* service = profile_.GetPrefs(); 501 service->SetBoolean(prefs::kInstantEnabled, true); 502 503 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("foo"), 504 NULL)); 505 506 // Tell the provider Instant is done. 507 provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), 508 InstantSuggestion(ASCIIToUTF16("bar"), 509 INSTANT_COMPLETE_NOW, 510 INSTANT_SUGGESTION_SEARCH, 511 string16())); 512 513 // The provider should now be done. 514 EXPECT_TRUE(provider_->done()); 515 516 // There should be two matches, one for what you typed, the other for 517 // 'foobar'. 518 EXPECT_EQ(2u, provider_->matches().size()); 519 GURL instant_url(default_t_url_->url_ref().ReplaceSearchTerms( 520 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("foobar")))); 521 AutocompleteMatch instant_match; 522 EXPECT_TRUE(FindMatchWithDestination(instant_url, &instant_match)); 523 524 // And the 'foobar' match should not have a description, it'll be set later. 525 EXPECT_TRUE(instant_match.description.empty()); 526 527 // Make sure the what you typed match has no description. 528 AutocompleteMatch wyt_match; 529 EXPECT_TRUE(FindMatchWithDestination( 530 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 531 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("foo")))), 532 &wyt_match)); 533 EXPECT_TRUE(wyt_match.description.empty()); 534 535 // The Instant search should be more relevant. 536 EXPECT_GT(instant_match.relevance, wyt_match.relevance); 537} 538 539// Make sure FinalizeInstantQuery works with URL suggestions. 540TEST_F(SearchProviderTest, FinalizeInstantURL) { 541 PrefService* service = profile_.GetPrefs(); 542 service->SetBoolean(prefs::kInstantEnabled, true); 543 544 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("ex"), 545 NULL)); 546 547 // Tell the provider Instant is done. 548 provider_->FinalizeInstantQuery(ASCIIToUTF16("ex"), 549 InstantSuggestion( 550 ASCIIToUTF16("http://example.com/"), 551 INSTANT_COMPLETE_NOW, 552 INSTANT_SUGGESTION_URL, 553 string16())); 554 555 // The provider should now be done. 556 EXPECT_TRUE(provider_->done()); 557 558 // There should be two matches, one for what you typed, the other for 559 // "http://example.com/". 560 EXPECT_EQ(2u, provider_->matches().size()); 561 GURL instant_url("http://example.com"); 562 AutocompleteMatch instant_match; 563 EXPECT_TRUE(FindMatchWithDestination(instant_url, &instant_match)); 564 565 // The Instant match should not have a description, it'll be set later. 566 EXPECT_TRUE(instant_match.description.empty()); 567 568 // Make sure the what you typed match has no description. 569 AutocompleteMatch wyt_match; 570 EXPECT_TRUE(FindMatchWithDestination( 571 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 572 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("ex")))), 573 &wyt_match)); 574 EXPECT_TRUE(wyt_match.description.empty()); 575 576 // The Instant URL should be more relevant. 577 EXPECT_GT(instant_match.relevance, wyt_match.relevance); 578} 579 580// An Instant URL suggestion should behave the same way whether the input text 581// is classified as UNKNOWN or as an URL. Otherwise if the user types 582// "example.co" url-what-you-typed will displace the Instant suggestion for 583// "example.com". 584TEST_F(SearchProviderTest, FinalizeInstantURLWithURLText) { 585 PrefService* service = profile_.GetPrefs(); 586 service->SetBoolean(prefs::kInstantEnabled, true); 587 588 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch( 589 ASCIIToUTF16("example.co"), NULL)); 590 591 // Tell the provider Instant is done. 592 provider_->FinalizeInstantQuery(ASCIIToUTF16("example.co"), 593 InstantSuggestion( 594 ASCIIToUTF16("http://example.com/"), 595 INSTANT_COMPLETE_NOW, 596 INSTANT_SUGGESTION_URL, 597 string16())); 598 599 // The provider should now be done. 600 EXPECT_TRUE(provider_->done()); 601 602 // There should be two matches, one for what you typed, the other for 603 // "http://example.com/". 604 EXPECT_EQ(2u, provider_->matches().size()); 605 GURL instant_url("http://example.com"); 606 AutocompleteMatch instant_match; 607 EXPECT_TRUE(FindMatchWithDestination(instant_url, &instant_match)); 608 609 // The Instant match should not have a description, it'll be set later. 610 EXPECT_TRUE(instant_match.description.empty()); 611 612 // The Instant URL should be more relevant than a URL_WHAT_YOU_TYPED match. 613 EXPECT_GT(instant_match.relevance, 614 HistoryURLProvider::kScoreForWhatYouTypedResult); 615} 616 617// Make sure that if FinalizeInstantQuery is invoked before suggest results 618// return, the suggest text from FinalizeInstantQuery is remembered. 619TEST_F(SearchProviderTest, RememberInstantQuery) { 620 PrefService* service = profile_.GetPrefs(); 621 service->SetBoolean(prefs::kInstantEnabled, true); 622 623 QueryForInput(ASCIIToUTF16("foo"), false); 624 625 // Finalize the Instant query immediately. 626 provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), 627 InstantSuggestion(ASCIIToUTF16("bar"), 628 INSTANT_COMPLETE_NOW, 629 INSTANT_SUGGESTION_SEARCH, 630 string16())); 631 632 // There should be two matches, one for what you typed, the other for 633 // 'foobar'. 634 EXPECT_EQ(2u, provider_->matches().size()); 635 GURL instant_url(default_t_url_->url_ref().ReplaceSearchTerms( 636 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("foobar")))); 637 AutocompleteMatch instant_match; 638 EXPECT_TRUE(FindMatchWithDestination(instant_url, &instant_match)); 639 640 // Wait until history and the suggest query complete. 641 profile_.BlockUntilHistoryProcessesPendingRequests(); 642 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 643 644 // Provider should be done. 645 EXPECT_TRUE(provider_->done()); 646 647 // There should be two matches, one for what you typed, the other for 648 // 'foobar'. 649 EXPECT_EQ(2u, provider_->matches().size()); 650 EXPECT_TRUE(FindMatchWithDestination(instant_url, &instant_match)); 651 652 // And the 'foobar' match should not have a description, it'll be set later. 653 EXPECT_TRUE(instant_match.description.empty()); 654} 655 656// Make sure that if trailing whitespace is added to the text supplied to 657// AutocompleteInput the default suggest text is cleared. 658TEST_F(SearchProviderTest, DifferingText) { 659 PrefService* service = profile_.GetPrefs(); 660 service->SetBoolean(prefs::kInstantEnabled, true); 661 662 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("foo"), 663 NULL)); 664 665 // Finalize the Instant query immediately. 666 provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), 667 InstantSuggestion(ASCIIToUTF16("bar"), 668 INSTANT_COMPLETE_NOW, 669 INSTANT_SUGGESTION_SEARCH, 670 string16())); 671 672 // Query with the same input text, but trailing whitespace. 673 AutocompleteMatch instant_match; 674 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("foo "), 675 &instant_match)); 676 677 // There should only one match, for what you typed. 678 EXPECT_EQ(1u, provider_->matches().size()); 679 EXPECT_FALSE(instant_match.destination_url.is_empty()); 680} 681 682TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) { 683 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( 684 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor); 685 GURL url = AddSearchToHistory(default_t_url_, 686 ASCIIToUTF16("docs.google.com"), 1); 687 688 // Add the term as a url. 689 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)-> 690 AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1, 691 base::Time::Now(), false, history::SOURCE_BROWSED); 692 profile_.BlockUntilHistoryProcessesPendingRequests(); 693 694 AutocompleteMatch wyt_match; 695 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"), 696 &wyt_match)); 697 698 // There should be two matches, one for what you typed, the other for 699 // 'docs.google.com'. The search term should have a lower priority than the 700 // what you typed match. 701 ASSERT_EQ(2u, provider_->matches().size()); 702 AutocompleteMatch term_match; 703 EXPECT_TRUE(FindMatchWithDestination(url, &term_match)); 704 EXPECT_GT(wyt_match.relevance, term_match.relevance); 705} 706 707// A multiword search with one visit should not autocomplete until multiple 708// words are typed. 709TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) { 710 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"), 711 1)); 712 profile_.BlockUntilHistoryProcessesPendingRequests(); 713 714 AutocompleteMatch wyt_match; 715 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"), 716 &wyt_match)); 717 ASSERT_EQ(2u, provider_->matches().size()); 718 AutocompleteMatch term_match; 719 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 720 EXPECT_GT(wyt_match.relevance, term_match.relevance); 721 722 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"), 723 &wyt_match)); 724 ASSERT_EQ(2u, provider_->matches().size()); 725 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 726 EXPECT_GT(term_match.relevance, wyt_match.relevance); 727} 728 729// A multiword search with more than one visit should autocomplete immediately. 730TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) { 731 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 732 2)); 733 profile_.BlockUntilHistoryProcessesPendingRequests(); 734 735 AutocompleteMatch wyt_match; 736 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"), 737 &wyt_match)); 738 ASSERT_EQ(2u, provider_->matches().size()); 739 AutocompleteMatch term_match; 740 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 741 EXPECT_GT(term_match.relevance, wyt_match.relevance); 742} 743 744// Autocompletion should work at a word boundary after a space. 745TEST_F(SearchProviderTest, AutocompleteAfterSpace) { 746 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 747 2)); 748 profile_.BlockUntilHistoryProcessesPendingRequests(); 749 750 AutocompleteMatch wyt_match; 751 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "), 752 &wyt_match)); 753 ASSERT_EQ(2u, provider_->matches().size()); 754 AutocompleteMatch term_match; 755 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 756 EXPECT_GT(term_match.relevance, wyt_match.relevance); 757} 758 759// Newer multiword searches should score more highly than older ones. 760TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) { 761 GURL term_url_a(AddSearchToHistory(default_t_url_, 762 ASCIIToUTF16("three searches aaa"), 1)); 763 GURL term_url_b(AddSearchToHistory(default_t_url_, 764 ASCIIToUTF16("three searches bbb"), 1)); 765 profile_.BlockUntilHistoryProcessesPendingRequests(); 766 767 AutocompleteMatch wyt_match; 768 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"), 769 &wyt_match)); 770 ASSERT_EQ(3u, provider_->matches().size()); 771 AutocompleteMatch term_match_a; 772 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 773 AutocompleteMatch term_match_b; 774 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 775 EXPECT_GT(term_match_b.relevance, term_match_a.relevance); 776 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 777} 778 779// An autocompleted multiword search should not be replaced by a different 780// autocompletion while the user is still typing a valid prefix. 781TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) { 782 GURL term_url_a(AddSearchToHistory(default_t_url_, 783 ASCIIToUTF16("four searches aaa"), 2)); 784 GURL term_url_b(AddSearchToHistory(default_t_url_, 785 ASCIIToUTF16("four searches bbb"), 1)); 786 profile_.BlockUntilHistoryProcessesPendingRequests(); 787 788 AutocompleteMatch wyt_match; 789 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"), 790 &wyt_match)); 791 ASSERT_EQ(3u, provider_->matches().size()); 792 AutocompleteMatch term_match_a; 793 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 794 AutocompleteMatch term_match_b; 795 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 796 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 797 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 798 799 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"), 800 &wyt_match)); 801 ASSERT_EQ(3u, provider_->matches().size()); 802 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 803 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 804 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 805 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 806} 807 808// Non-completable multiword searches should not crowd out single-word searches. 809TEST_F(SearchProviderTest, DontCrowdOutSingleWords) { 810 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1)); 811 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1); 812 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1); 813 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1); 814 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1); 815 profile_.BlockUntilHistoryProcessesPendingRequests(); 816 817 AutocompleteMatch wyt_match; 818 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"), 819 &wyt_match)); 820 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size()); 821 AutocompleteMatch term_match; 822 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 823 EXPECT_GT(term_match.relevance, wyt_match.relevance); 824} 825 826// Inline autocomplete matches regardless of case differences from the input. 827TEST_F(SearchProviderTest, InlineMixedCaseMatches) { 828 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1)); 829 profile_.BlockUntilHistoryProcessesPendingRequests(); 830 831 AutocompleteMatch wyt_match; 832 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"), 833 &wyt_match)); 834 ASSERT_EQ(2u, provider_->matches().size()); 835 AutocompleteMatch term_match; 836 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 837 EXPECT_GT(term_match.relevance, wyt_match.relevance); 838 EXPECT_EQ(1u, term_match.inline_autocomplete_offset); 839 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit); 840} 841 842// Verifies AutocompleteControllers return results (including keyword 843// results) in the right order and set descriptions for them correctly. 844TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) { 845 // Add an entry that corresponds to a keyword search with 'term2'. 846 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1); 847 profile_.BlockUntilHistoryProcessesPendingRequests(); 848 849 AutocompleteController controller(&profile_, NULL, 850 AutocompleteProvider::TYPE_SEARCH); 851 controller.Start(AutocompleteInput( 852 ASCIIToUTF16("k t"), string16::npos, string16(), GURL(), false, false, 853 true, AutocompleteInput::ALL_MATCHES)); 854 const AutocompleteResult& result = controller.result(); 855 856 // There should be three matches, one for the keyword history, one for 857 // keyword provider's what-you-typed, and one for the default provider's 858 // what you typed, in that order. 859 ASSERT_EQ(3u, result.size()); 860 EXPECT_EQ(AutocompleteMatch::SEARCH_HISTORY, result.match_at(0).type); 861 EXPECT_EQ(AutocompleteMatch::SEARCH_OTHER_ENGINE, result.match_at(1).type); 862 EXPECT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, result.match_at(2).type); 863 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance); 864 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance); 865 866 // The two keyword results should come with the keyword we expect. 867 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword); 868 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword); 869 // The default provider has a different keyword. (We don't explicitly 870 // set it during this test, so all we do is assert that it's different.) 871 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword); 872 873 // The top result will always have a description. The third result, 874 // coming from a different provider than the first two, should also. 875 // Whether the second result has one doesn't matter much. (If it was 876 // missing, people would infer that it's the same search provider as 877 // the one above it.) 878 EXPECT_FALSE(result.match_at(0).description.empty()); 879 EXPECT_FALSE(result.match_at(2).description.empty()); 880 EXPECT_NE(result.match_at(0).description, result.match_at(2).description); 881} 882 883TEST_F(SearchProviderTest, KeywordVerbatim) { 884 TestData cases[] = { 885 // Test a simple keyword input. 886 { ASCIIToUTF16("k foo"), 2, 887 { ResultInfo(GURL("http://keyword/foo"), 888 AutocompleteMatch::SEARCH_OTHER_ENGINE, 889 ASCIIToUTF16("k foo")), 890 ResultInfo(GURL("http://defaultturl/k%20foo"), 891 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 892 ASCIIToUTF16("k foo") ) } }, 893 894 // Make sure extra whitespace after the keyword doesn't change the 895 // keyword verbatim query. 896 { ASCIIToUTF16("k foo"), 2, 897 { ResultInfo(GURL("http://keyword/foo"), 898 AutocompleteMatch::SEARCH_OTHER_ENGINE, 899 ASCIIToUTF16("k foo")), 900 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"), 901 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 902 ASCIIToUTF16("k foo")) } }, 903 // Leading whitespace should be stripped before SearchProvider gets the 904 // input; hence there are no tests here about how it handles those inputs. 905 906 // But whitespace elsewhere in the query string should matter to both 907 // matches. 908 { ASCIIToUTF16("k foo bar"), 2, 909 { ResultInfo(GURL("http://keyword/foo%20%20bar"), 910 AutocompleteMatch::SEARCH_OTHER_ENGINE, 911 ASCIIToUTF16("k foo bar")), 912 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"), 913 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 914 ASCIIToUTF16("k foo bar")) } }, 915 // Note in the above test case we don't test trailing whitespace because 916 // SearchProvider still doesn't handle this well. See related bugs: 917 // 102690, 99239, 164635. 918 919 // Keywords can be prefixed by certain things that should get ignored 920 // when constructing the keyword match. 921 { ASCIIToUTF16("www.k foo"), 2, 922 { ResultInfo(GURL("http://keyword/foo"), 923 AutocompleteMatch::SEARCH_OTHER_ENGINE, 924 ASCIIToUTF16("k foo")), 925 ResultInfo(GURL("http://defaultturl/www.k%20foo"), 926 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 927 ASCIIToUTF16("www.k foo")) } }, 928 { ASCIIToUTF16("http://k foo"), 2, 929 { ResultInfo(GURL("http://keyword/foo"), 930 AutocompleteMatch::SEARCH_OTHER_ENGINE, 931 ASCIIToUTF16("k foo")), 932 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"), 933 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 934 ASCIIToUTF16("http://k foo")) } }, 935 { ASCIIToUTF16("http://www.k foo"), 2, 936 { ResultInfo(GURL("http://keyword/foo"), 937 AutocompleteMatch::SEARCH_OTHER_ENGINE, 938 ASCIIToUTF16("k foo")), 939 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"), 940 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 941 ASCIIToUTF16("http://www.k foo")) } }, 942 943 // A keyword with no remaining input shouldn't get a keyword 944 // verbatim match. 945 { ASCIIToUTF16("k"), 1, 946 { ResultInfo(GURL("http://defaultturl/k"), 947 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 948 ASCIIToUTF16("k")) } }, 949 { ASCIIToUTF16("k "), 1, 950 { ResultInfo(GURL("http://defaultturl/k%20"), 951 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 952 ASCIIToUTF16("k ")) } } 953 954 // The fact that verbatim queries to keyword are handled by KeywordProvider 955 // not SearchProvider is tested in 956 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc. 957 }; 958 959 // Test not in keyword mode. 960 RunTest(cases, arraysize(cases), false); 961 962 // Test in keyword mode. (Both modes should give the same result.) 963 RunTest(cases, arraysize(cases), true); 964} 965 966// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on. 967// Also verifies that just the *first* navigational result is listed as a match 968// if suggested relevance scores were not sent. 969TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) { 970 QueryForInput(ASCIIToUTF16("a.c"), false); 971 972 // Make sure the default providers suggest service was queried. 973 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 974 SearchProvider::kDefaultProviderURLFetcherID); 975 ASSERT_TRUE(fetcher); 976 977 // Tell the SearchProvider the suggest query is done. 978 fetcher->set_response_code(200); 979 fetcher->SetResponseString( 980 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 981 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 982 fetcher->delegate()->OnURLFetchComplete(fetcher); 983 fetcher = NULL; 984 985 // Run till the history results complete. 986 RunTillProviderDone(); 987 988 // Make sure the only match is 'a.com' and it doesn't have a template_url. 989 AutocompleteMatch nav_match; 990 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 991 EXPECT_TRUE(nav_match.keyword.empty()); 992 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 993} 994 995// Verifies that the most relevant suggest results are added properly. 996TEST_F(SearchProviderTest, SuggestRelevance) { 997 QueryForInput(ASCIIToUTF16("a"), false); 998 999 // Make sure the default provider's suggest service was queried. 1000 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1001 SearchProvider::kDefaultProviderURLFetcherID); 1002 ASSERT_TRUE(fetcher); 1003 1004 // Tell the SearchProvider the suggest query is done. 1005 fetcher->set_response_code(200); 1006 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 1007 fetcher->delegate()->OnURLFetchComplete(fetcher); 1008 fetcher = NULL; 1009 1010 // Run till the history results complete. 1011 RunTillProviderDone(); 1012 1013 // Check the expected verbatim and (first 3) suggestions' relative relevances. 1014 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 1015 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 1016 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 1017 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 1018 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 1019 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 1020 EXPECT_GT(verbatim.relevance, match_a1.relevance); 1021 EXPECT_GT(match_a1.relevance, match_a2.relevance); 1022 EXPECT_GT(match_a2.relevance, match_a3.relevance); 1023} 1024 1025// Verifies that suggest results with relevance scores are added 1026// properly when using the default fetcher. When adding a new test 1027// case to this test, please consider adding it to the tests in 1028// KeywordFetcherSuggestRelevance below. 1029TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) { 1030 const std::string kNotApplicable("Not Applicable"); 1031 struct { 1032 const std::string json; 1033 const std::string matches[4]; 1034 } cases[] = { 1035 // Ensure that suggestrelevance scores reorder matches. 1036 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1037 { "a", "c", "b", kNotApplicable } }, 1038 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1039 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1040 "\"google:suggestrelevance\":[1, 2]}]", 1041 { "a", "c.com", "b.com", kNotApplicable } }, 1042 1043 // Without suggested relevance scores, we should only allow one 1044 // navsuggest result to be be displayed. 1045 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1046 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1047 { "a", "b.com", kNotApplicable, kNotApplicable } }, 1048 1049 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1050 // Negative values will have no effect; the calculated value will be used. 1051 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1052 "\"google:suggestrelevance\":[9998]}]", 1053 { "a", "a1", kNotApplicable, kNotApplicable } }, 1054 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1055 "\"google:suggestrelevance\":[9999]}]", 1056 { "a1", "a", kNotApplicable, kNotApplicable } }, 1057 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1058 "\"google:suggestrelevance\":[9999]}]", 1059 { "a1", kNotApplicable, kNotApplicable, kNotApplicable } }, 1060 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1061 "\"google:suggestrelevance\":[9999]}]", 1062 { "a1", "a", kNotApplicable, kNotApplicable } }, 1063 { "[\"a\",[\"http://a.com\"],[],[]," 1064 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1065 "\"google:verbatimrelevance\":9999," 1066 "\"google:suggestrelevance\":[9998]}]", 1067 { "a", "a.com", kNotApplicable, kNotApplicable } }, 1068 { "[\"a\",[\"http://a.com\"],[],[]," 1069 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1070 "\"google:verbatimrelevance\":9998," 1071 "\"google:suggestrelevance\":[9999]}]", 1072 { "a.com", "a", kNotApplicable, kNotApplicable } }, 1073 { "[\"a\",[\"http://a.com\"],[],[]," 1074 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1075 "\"google:verbatimrelevance\":0," 1076 "\"google:suggestrelevance\":[9999]}]", 1077 { "a.com", kNotApplicable, kNotApplicable, kNotApplicable } }, 1078 { "[\"a\",[\"http://a.com\"],[],[]," 1079 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1080 "\"google:verbatimrelevance\":-1," 1081 "\"google:suggestrelevance\":[9999]}]", 1082 { "a.com", "a", kNotApplicable, kNotApplicable } }, 1083 1084 // Ensure that both types of relevance scores reorder matches together. 1085 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1086 "\"google:verbatimrelevance\":9998}]", 1087 { "a1", "a", "a2", kNotApplicable } }, 1088 1089 // Ensure that only inlinable matches may be ranked as the highest result. 1090 // Ignore all suggested relevance scores if this constraint is violated. 1091 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1092 { "a", "b", kNotApplicable, kNotApplicable } }, 1093 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1094 "\"google:verbatimrelevance\":0}]", 1095 { "a", "b", kNotApplicable, kNotApplicable } }, 1096 { "[\"a\",[\"http://b.com\"],[],[]," 1097 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1098 "\"google:suggestrelevance\":[9999]}]", 1099 { "a", "b.com", kNotApplicable, kNotApplicable } }, 1100 { "[\"a\",[\"http://b.com\"],[],[]," 1101 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1102 "\"google:suggestrelevance\":[9999]," 1103 "\"google:verbatimrelevance\":0}]", 1104 { "a", "b.com", kNotApplicable, kNotApplicable } }, 1105 1106 // Ensure that the top result is ranked as highly as calculated verbatim. 1107 // Ignore the suggested verbatim relevance if this constraint is violated. 1108 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1109 { "a", "a1", kNotApplicable, kNotApplicable } }, 1110 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1111 { "a", "a1", kNotApplicable, kNotApplicable } }, 1112 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1113 "\"google:verbatimrelevance\":0}]", 1114 { "a", "a1", kNotApplicable, kNotApplicable } }, 1115 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1116 "\"google:verbatimrelevance\":0}]", 1117 { "a", "a2", "a1", kNotApplicable } }, 1118 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1119 "\"google:verbatimrelevance\":2}]", 1120 { "a", "a2", "a1", kNotApplicable } }, 1121 { "[\"a\",[\"http://a.com\"],[],[]," 1122 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1123 "\"google:suggestrelevance\":[1]," 1124 "\"google:verbatimrelevance\":0}]", 1125 { "a", "a.com", kNotApplicable, kNotApplicable } }, 1126 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1127 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1128 "\"google:suggestrelevance\":[1, 2]," 1129 "\"google:verbatimrelevance\":0}]", 1130 { "a", "a2.com", "a1.com", kNotApplicable } }, 1131 1132 // Ensure that all suggestions are considered, regardless of order. 1133 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1134 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1135 { "a", "h", "g", "f" } }, 1136 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1137 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1138 "\"http://h.com\"],[],[]," 1139 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1140 "\"NAVIGATION\", \"NAVIGATION\"," 1141 "\"NAVIGATION\", \"NAVIGATION\"," 1142 "\"NAVIGATION\"]," 1143 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1144 { "a", "h.com", "g.com", "f.com" } }, 1145 1146 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1147 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1148 { "a", "a1", "a2", kNotApplicable } }, 1149 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1150 { "a", "a1", kNotApplicable, kNotApplicable } }, 1151 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1152 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1153 "\"google:suggestrelevance\":[1]}]", 1154 { "a", "a1.com", kNotApplicable, kNotApplicable } }, 1155 { "[\"a\",[\"http://a1.com\"],[],[]," 1156 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1157 "\"google:suggestrelevance\":[9999, 1]}]", 1158 { "a", "a1.com", kNotApplicable, kNotApplicable } }, 1159 1160 // Ensure that all 'verbatim' results are merged with their maximum score. 1161 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1162 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1163 { "a2", "a", "a1", kNotApplicable } }, 1164 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1165 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1166 "\"google:verbatimrelevance\":0}]", 1167 { "a2", "a", "a1", kNotApplicable } }, 1168 1169 // Ensure that verbatim is always generated without other suggestions. 1170 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1171 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1172 { "a", kNotApplicable, kNotApplicable, kNotApplicable } }, 1173 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1174 { "a", kNotApplicable, kNotApplicable, kNotApplicable } }, 1175 }; 1176 1177 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1178 QueryForInput(ASCIIToUTF16("a"), false); 1179 net::TestURLFetcher* fetcher = WaitUntilURLFetcherIsReady( 1180 SearchProvider::kDefaultProviderURLFetcherID); 1181 ASSERT_TRUE(fetcher); 1182 fetcher->set_response_code(200); 1183 fetcher->SetResponseString(cases[i].json); 1184 fetcher->delegate()->OnURLFetchComplete(fetcher); 1185 RunTillProviderDone(); 1186 1187 const std::string description = "for input with json=" + cases[i].json; 1188 const ACMatches& matches = provider_->matches(); 1189 // The top match must inline and score as highly as calculated verbatim. 1190 EXPECT_NE(string16::npos, matches[0].inline_autocomplete_offset) << 1191 description; 1192 EXPECT_GE(matches[0].relevance, 1300) << description; 1193 1194 size_t j = 0; 1195 // Ensure that the returned matches equal the expectations. 1196 for (; j < matches.size(); ++j) 1197 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1198 matches[j].contents) << description; 1199 // Ensure that no expected matches are missing. 1200 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1201 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 1202 "Case # " << i << " " << description; 1203 } 1204} 1205 1206// Verifies that suggest results with relevance scores are added 1207// properly when using the keyword fetcher. This is similar to the 1208// test DefaultFetcherSuggestRelevance above but this uses inputs that 1209// trigger keyword suggestions (i.e., "k a" rather than "a") and has 1210// different expectations (because now the results are a mix of 1211// keyword suggestions and default provider suggestions. When a new 1212// test is added to this TEST_F, please consider if it would be 1213// appropriate to add to DefaultFetcherSuggestRelevance as well. 1214TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1215 const std::string kNotApplicable("Not Applicable"); 1216 struct { 1217 const std::string json; 1218 const struct { 1219 const std::string contents; 1220 const bool from_keyword; 1221 } matches[5]; 1222 } cases[] = { 1223 // Ensure that suggest relevance scores reorder matches and that 1224 // the keyword verbatim (lacking a suggested verbatim score) beats 1225 // the default provider verbatim. 1226 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1227 { { "a", true }, 1228 { "k a", false }, 1229 { "c", true }, 1230 { "b", true }, 1231 { kNotApplicable, false } } }, 1232 // Again, check that relevance scores reorder matches, just this 1233 // time with navigation matches. This also checks that with 1234 // suggested relevance scores we allow multiple navsuggest results. 1235 // It's odd that navsuggest results that come from a keyword 1236 // provider are marked as not a keyword result. I think this 1237 // comes from them not going to a keyword search engine). 1238 // TODO(mpearson): Investigate the implications (if any) of 1239 // tagging these results appropriately. If so, do it because it 1240 // makes more sense. 1241 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1242 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1243 "\"google:suggestrelevance\":[1, 2]}]", 1244 { { "a", true }, 1245 { "k a", false }, 1246 { "c.com", false }, 1247 { "b.com", false }, 1248 { kNotApplicable, false } } }, 1249 1250 // Without suggested relevance scores, we should only allow one 1251 // navsuggest result to be be displayed. 1252 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1253 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1254 { { "a", true }, 1255 { "b.com", false }, 1256 { "k a", false }, 1257 { kNotApplicable, false }, 1258 { kNotApplicable, false } } }, 1259 1260 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1261 // Negative values will have no effect; the calculated value will be used. 1262 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1263 "\"google:suggestrelevance\":[9998]}]", 1264 { { "a", true }, 1265 { "a1", true }, 1266 { "k a", false }, 1267 { kNotApplicable, false }, 1268 { kNotApplicable, false } } }, 1269 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1270 "\"google:suggestrelevance\":[9999]}]", 1271 { { "a1", true }, 1272 { "a", true }, 1273 { "k a", false }, 1274 { kNotApplicable, false }, 1275 { kNotApplicable, false } } }, 1276 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1277 "\"google:suggestrelevance\":[9999]}]", 1278 { { "a1", true }, 1279 { "k a", false }, 1280 { kNotApplicable, false }, 1281 { kNotApplicable, false }, 1282 { kNotApplicable, false } } }, 1283 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1284 "\"google:suggestrelevance\":[9999]}]", 1285 { { "a1", true }, 1286 { "a", true }, 1287 { "k a", false }, 1288 { kNotApplicable, false }, 1289 { kNotApplicable, false } } }, 1290 { "[\"a\",[\"http://a.com\"],[],[]," 1291 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1292 "\"google:verbatimrelevance\":9999," 1293 "\"google:suggestrelevance\":[9998]}]", 1294 { { "a", true }, 1295 { "a.com", false }, 1296 { "k a", false }, 1297 { kNotApplicable, false }, 1298 { kNotApplicable, false } } }, 1299 { "[\"a\",[\"http://a.com\"],[],[]," 1300 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1301 "\"google:verbatimrelevance\":9998," 1302 "\"google:suggestrelevance\":[9999]}]", 1303 { { "a.com", false }, 1304 { "a", true }, 1305 { "k a", false }, 1306 { kNotApplicable, false }, 1307 { kNotApplicable, false } } }, 1308 { "[\"a\",[\"http://a.com\"],[],[]," 1309 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1310 "\"google:verbatimrelevance\":0," 1311 "\"google:suggestrelevance\":[9999]}]", 1312 { { "a.com", false }, 1313 { "k a", false }, 1314 { kNotApplicable, false }, 1315 { kNotApplicable, false }, 1316 { kNotApplicable, false } } }, 1317 { "[\"a\",[\"http://a.com\"],[],[]," 1318 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1319 "\"google:verbatimrelevance\":-1," 1320 "\"google:suggestrelevance\":[9999]}]", 1321 { { "a.com", false }, 1322 { "a", true }, 1323 { "k a", false }, 1324 { kNotApplicable, false }, 1325 { kNotApplicable, false } } }, 1326 1327 // Ensure that both types of relevance scores reorder matches together. 1328 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1329 "\"google:verbatimrelevance\":9998}]", 1330 { { "a1", true }, 1331 { "a", true }, 1332 { "a2", true }, 1333 { "k a", false }, 1334 { kNotApplicable, false } } }, 1335 1336 // Ensure that only inlinable matches may be ranked as the highest result. 1337 // Ignore all suggested relevance scores if this constraint is violated. 1338 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1339 { { "a", true }, 1340 { "b", true }, 1341 { "k a", false }, 1342 { kNotApplicable, false }, 1343 { kNotApplicable, false } } }, 1344 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1345 "\"google:verbatimrelevance\":0}]", 1346 { { "a", true }, 1347 { "b", true }, 1348 { "k a", false }, 1349 { kNotApplicable, false }, 1350 { kNotApplicable, false } } }, 1351 { "[\"a\",[\"http://b.com\"],[],[]," 1352 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1353 "\"google:suggestrelevance\":[9999]}]", 1354 { { "a", true }, 1355 { "b.com", false }, 1356 { "k a", false }, 1357 { kNotApplicable, false }, 1358 { kNotApplicable, false } } }, 1359 { "[\"a\",[\"http://b.com\"],[],[]," 1360 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1361 "\"google:suggestrelevance\":[9999]," 1362 "\"google:verbatimrelevance\":0}]", 1363 { { "a", true }, 1364 { "b.com", false }, 1365 { "k a", false }, 1366 { kNotApplicable, false }, 1367 { kNotApplicable, false } } }, 1368 1369 // Ensure that the top result is ranked as highly as calculated verbatim. 1370 // Ignore the suggested verbatim relevance if this constraint is violated. 1371 // Note that keyword suggestions by default (not in suggested relevance 1372 // mode) score more highly than the default verbatim. 1373 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1374 { { "a", true }, 1375 { "a1", true }, 1376 { "k a", false }, 1377 { kNotApplicable, false }, 1378 { kNotApplicable, false } } }, 1379 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1380 { { "a", true }, 1381 { "a1", true }, 1382 { "k a", false }, 1383 { kNotApplicable, false }, 1384 { kNotApplicable, false } } }, 1385 // Continuing the same category of tests, but make sure we keep the 1386 // suggested relevance scores even as we discard the verbatim relevance 1387 // scores. 1388 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1389 "\"google:verbatimrelevance\":0}]", 1390 { { "a", true }, 1391 { "k a", false }, 1392 { "a1", true }, 1393 { kNotApplicable, false }, 1394 { kNotApplicable, false } } }, 1395 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1396 "\"google:verbatimrelevance\":0}]", 1397 { { "a", true }, 1398 { "k a", false }, 1399 { "a2", true }, 1400 { "a1", true }, 1401 { kNotApplicable, false } } }, 1402 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1403 "\"google:verbatimrelevance\":2}]", 1404 { { "a", true }, 1405 { "k a", false }, 1406 { "a2", true }, 1407 { "a1", true }, 1408 { kNotApplicable, false } } }, 1409 { "[\"a\",[\"http://a.com\"],[],[]," 1410 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1411 "\"google:suggestrelevance\":[1]," 1412 "\"google:verbatimrelevance\":0}]", 1413 { { "a", true }, 1414 { "k a", false }, 1415 { "a.com", false }, 1416 { kNotApplicable, false }, 1417 { kNotApplicable, false } } }, 1418 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1419 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1420 "\"google:suggestrelevance\":[1, 2]," 1421 "\"google:verbatimrelevance\":0}]", 1422 { { "a", true }, 1423 { "k a", false }, 1424 { "a2.com", false }, 1425 { "a1.com", false }, 1426 { kNotApplicable, false } } }, 1427 1428 // Ensure that all suggestions are considered, regardless of order. 1429 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1430 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1431 { { "a", true }, 1432 { "k a", false }, 1433 { "h", true }, 1434 { "g", true }, 1435 { "f", true } } }, 1436 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1437 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1438 "\"http://h.com\"],[],[]," 1439 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1440 "\"NAVIGATION\", \"NAVIGATION\"," 1441 "\"NAVIGATION\", \"NAVIGATION\"," 1442 "\"NAVIGATION\"]," 1443 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1444 { { "a", true }, 1445 { "k a", false }, 1446 { "h.com", false }, 1447 { "g.com", false }, 1448 { "f.com", false } } }, 1449 1450 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1451 // Note that keyword suggestions by default (not in suggested relevance 1452 // mode) score more highly than the default verbatim. 1453 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1454 { { "a", true }, 1455 { "a1", true }, 1456 { "a2", true }, 1457 { "k a", false }, 1458 { kNotApplicable, false } } }, 1459 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1460 { { "a", true }, 1461 { "a1", true }, 1462 { "k a", false }, 1463 { kNotApplicable, false }, 1464 { kNotApplicable, false } } }, 1465 // In this case, ignored the suggested relevance scores means we keep 1466 // only one navsuggest result. 1467 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1468 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1469 "\"google:suggestrelevance\":[1]}]", 1470 { { "a", true }, 1471 { "a1.com", false }, 1472 { "k a", false }, 1473 { kNotApplicable, false }, 1474 { kNotApplicable, false } } }, 1475 { "[\"a\",[\"http://a1.com\"],[],[]," 1476 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1477 "\"google:suggestrelevance\":[9999, 1]}]", 1478 { { "a", true }, 1479 { "a1.com", false }, 1480 { "k a", false }, 1481 { kNotApplicable, false }, 1482 { kNotApplicable, false } } }, 1483 1484 // Ensure that all 'verbatim' results are merged with their maximum score. 1485 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1486 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1487 { { "a2", true }, 1488 { "a", true }, 1489 { "a1", true }, 1490 { "k a", false }, 1491 { kNotApplicable, false } } }, 1492 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1493 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1494 "\"google:verbatimrelevance\":0}]", 1495 { { "a2", true }, 1496 { "a", true }, 1497 { "a1", true }, 1498 { "k a", false }, 1499 { kNotApplicable, false } } }, 1500 1501 // Ensure that verbatim is always generated without other suggestions. 1502 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1503 // (except when suggested relevances are ignored). 1504 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1505 { { "a", true }, 1506 { "k a", false }, 1507 { kNotApplicable, false }, 1508 { kNotApplicable, false }, 1509 { kNotApplicable, false } } }, 1510 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1511 { { "a", true }, 1512 { "k a", false }, 1513 { kNotApplicable, false }, 1514 { kNotApplicable, false }, 1515 { kNotApplicable, false } } }, 1516 }; 1517 1518 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1519 QueryForInput(ASCIIToUTF16("k a"), false); 1520 1521 // Set up a default fetcher with no results. 1522 net::TestURLFetcher* default_fetcher = WaitUntilURLFetcherIsReady( 1523 SearchProvider::kDefaultProviderURLFetcherID); 1524 ASSERT_TRUE(default_fetcher); 1525 default_fetcher->set_response_code(200); 1526 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1527 default_fetcher = NULL; 1528 1529 // Set up a keyword fetcher with provided results. 1530 net::TestURLFetcher* keyword_fetcher = WaitUntilURLFetcherIsReady( 1531 SearchProvider::kKeywordProviderURLFetcherID); 1532 ASSERT_TRUE(keyword_fetcher); 1533 keyword_fetcher->set_response_code(200); 1534 keyword_fetcher->SetResponseString(cases[i].json); 1535 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1536 keyword_fetcher = NULL; 1537 RunTillProviderDone(); 1538 1539 const std::string description = "for input with json=" + cases[i].json; 1540 const ACMatches& matches = provider_->matches(); 1541 // The top match must inline and score as highly as calculated verbatim. 1542 EXPECT_NE(string16::npos, matches[0].inline_autocomplete_offset) << 1543 description; 1544 EXPECT_GE(matches[0].relevance, 1300) << description; 1545 1546 size_t j = 0; 1547 // Ensure that the returned matches equal the expectations. 1548 for (; j < matches.size(); ++j) { 1549 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1550 matches[j].contents) << description; 1551 EXPECT_EQ(cases[i].matches[j].from_keyword, 1552 matches[j].keyword == ASCIIToUTF16("k")) << description; 1553 } 1554 // Ensure that no expected matches are missing. 1555 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1556 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1557 "Case # " << i << " " << description; 1558 } 1559} 1560 1561// Verifies suggest relevance behavior for URL input. 1562TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 1563 const std::string kNotApplicable("Not Applicable"); 1564 struct { 1565 const std::string input; 1566 const std::string json; 1567 const std::string match_contents[4]; 1568 const AutocompleteMatch::Type match_types[4]; 1569 } cases[] = { 1570 // Ensure topmost NAVIGATION matches are allowed for URL input. 1571 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 1572 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1573 "\"google:suggestrelevance\":[9999]}]", 1574 { "a.com/a", "a.com", kNotApplicable, kNotApplicable }, 1575 { AutocompleteMatch::NAVSUGGEST, AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1576 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 1577 1578 // Ensure topmost SUGGEST matches are not allowed for URL input. 1579 // SearchProvider disregards search and verbatim suggested relevances. 1580 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 1581 "{\"google:suggestrelevance\":[9999]}]", 1582 { "a.com", "a.com info", kNotApplicable, kNotApplicable }, 1583 { AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1584 AutocompleteMatch::SEARCH_SUGGEST, 1585 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 1586 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[]," 1587 "{\"google:suggestrelevance\":[9999]}]", 1588 { "a.com", "a.com/a", kNotApplicable, kNotApplicable }, 1589 { AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1590 AutocompleteMatch::SEARCH_SUGGEST, 1591 AutocompleteMatch::NUM_TYPES, AutocompleteMatch::NUM_TYPES } }, 1592 1593 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 1594 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1595 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1596 "\"google:suggestrelevance\":[9999, 9998]}]", 1597 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 1598 { AutocompleteMatch::NAVSUGGEST, 1599 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1600 AutocompleteMatch::SEARCH_SUGGEST, 1601 AutocompleteMatch::NUM_TYPES } }, 1602 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1603 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1604 "\"google:suggestrelevance\":[9998, 9997]," 1605 "\"google:verbatimrelevance\":9999}]", 1606 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 1607 { AutocompleteMatch::NAVSUGGEST, 1608 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1609 AutocompleteMatch::SEARCH_SUGGEST, 1610 AutocompleteMatch::NUM_TYPES } }, 1611 1612 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches. 1613 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1614 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1615 "\"google:suggestrelevance\":[9999, 9998]}]", 1616 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 1617 { AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1618 AutocompleteMatch::NAVSUGGEST, 1619 AutocompleteMatch::SEARCH_SUGGEST, 1620 AutocompleteMatch::NUM_TYPES } }, 1621 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1622 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1623 "\"google:suggestrelevance\":[9998, 9997]," 1624 "\"google:verbatimrelevance\":9999}]", 1625 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 1626 { AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, 1627 AutocompleteMatch::NAVSUGGEST, 1628 AutocompleteMatch::SEARCH_SUGGEST, 1629 AutocompleteMatch::NUM_TYPES } }, 1630 }; 1631 1632 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1633 QueryForInput(ASCIIToUTF16(cases[i].input), false); 1634 net::TestURLFetcher* fetcher = WaitUntilURLFetcherIsReady( 1635 SearchProvider::kDefaultProviderURLFetcherID); 1636 ASSERT_TRUE(fetcher); 1637 fetcher->set_response_code(200); 1638 fetcher->SetResponseString(cases[i].json); 1639 fetcher->delegate()->OnURLFetchComplete(fetcher); 1640 RunTillProviderDone(); 1641 1642 size_t j = 0; 1643 const ACMatches& matches = provider_->matches(); 1644 // Ensure that the returned matches equal the expectations. 1645 for (; j < matches.size(); ++j) { 1646 EXPECT_EQ(ASCIIToUTF16(cases[i].match_contents[j]), matches[j].contents); 1647 EXPECT_EQ(cases[i].match_types[j], matches[j].type); 1648 } 1649 // Ensure that no expected matches are missing. 1650 for (; j < ARRAYSIZE_UNSAFE(cases[i].match_contents); ++j) { 1651 EXPECT_EQ(kNotApplicable, cases[i].match_contents[j]); 1652 EXPECT_EQ(AutocompleteMatch::NUM_TYPES, cases[i].match_types[j]); 1653 } 1654 } 1655} 1656 1657// A basic test that verifies the field trial triggered parsing logic. 1658TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 1659 QueryForInput(ASCIIToUTF16("foo"), false); 1660 1661 // Make sure the default providers suggest service was queried. 1662 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1663 SearchProvider::kDefaultProviderURLFetcherID); 1664 ASSERT_TRUE(fetcher); 1665 1666 // Tell the SearchProvider the suggest query is done. 1667 fetcher->set_response_code(200); 1668 fetcher->SetResponseString( 1669 "[\"foo\",[\"foo bar\"],[\"\"],[]," 1670 "{\"google:suggesttype\":[\"QUERY\"]," 1671 "\"google:fieldtrialtriggered\":true}]"); 1672 fetcher->delegate()->OnURLFetchComplete(fetcher); 1673 fetcher = NULL; 1674 1675 // Run till the history results complete. 1676 RunTillProviderDone(); 1677 1678 { 1679 // Check for the match and field trial triggered bits. 1680 AutocompleteMatch match; 1681 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 1682 ProvidersInfo providers_info; 1683 provider_->AddProviderInfo(&providers_info); 1684 ASSERT_EQ(1U, providers_info.size()); 1685 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 1686 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 1687 } 1688 { 1689 // Reset the session and check that bits are reset. 1690 provider_->ResetSession(); 1691 ProvidersInfo providers_info; 1692 provider_->AddProviderInfo(&providers_info); 1693 ASSERT_EQ(1U, providers_info.size()); 1694 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 1695 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 1696 } 1697} 1698 1699// Verifies inline autocompletion of navigational results. 1700TEST_F(SearchProviderTest, NavigationInline) { 1701 struct { 1702 const std::string input; 1703 const std::string url; 1704 // Test the expected fill_into_edit, which may drop "http://". 1705 // Some cases do not trim "http://" to match from the start of the scheme. 1706 const std::string fill_into_edit; 1707 size_t inline_offset; 1708 } cases[] = { 1709 // Do not inline matches that do not contain the input; trim http as needed. 1710 { "x", "http://www.abc.com", 1711 "www.abc.com", string16::npos }, 1712 { "https:", "http://www.abc.com", 1713 "www.abc.com", string16::npos }, 1714 { "abc.com/", "http://www.abc.com", 1715 "www.abc.com", string16::npos }, 1716 { "http://www.abc.com/a", "http://www.abc.com", 1717 "http://www.abc.com", string16::npos }, 1718 { "http://www.abc.com", "https://www.abc.com", 1719 "https://www.abc.com", string16::npos }, 1720 { "http://abc.com", "ftp://abc.com", 1721 "ftp://abc.com", string16::npos }, 1722 { "https://www.abc.com", "http://www.abc.com", 1723 "www.abc.com", string16::npos }, 1724 { "ftp://abc.com", "http://abc.com", 1725 "abc.com", string16::npos }, 1726 1727 // Do not inline matches with invalid input prefixes; trim http as needed. 1728 { "ttp", "http://www.abc.com", 1729 "www.abc.com", string16::npos }, 1730 { "://w", "http://www.abc.com", 1731 "www.abc.com", string16::npos }, 1732 { "ww.", "http://www.abc.com", 1733 "www.abc.com", string16::npos }, 1734 { ".ab", "http://www.abc.com", 1735 "www.abc.com", string16::npos }, 1736 { "bc", "http://www.abc.com", 1737 "www.abc.com", string16::npos }, 1738 { ".com", "http://www.abc.com", 1739 "www.abc.com", string16::npos }, 1740 1741 // Do not inline matches that omit input domain labels; trim http as needed. 1742 { "www.a", "http://a.com", 1743 "a.com", string16::npos }, 1744 { "http://www.a", "http://a.com", 1745 "http://a.com", string16::npos }, 1746 { "www.a", "ftp://a.com", 1747 "ftp://a.com", string16::npos }, 1748 { "ftp://www.a", "ftp://a.com", 1749 "ftp://a.com", string16::npos }, 1750 1751 // Input matching but with nothing to inline will not yield an offset. 1752 { "abc.com", "http://www.abc.com", 1753 "www.abc.com", string16::npos }, 1754 { "http://www.abc.com", "http://www.abc.com", 1755 "http://www.abc.com", string16::npos }, 1756 1757 // Inline matches when the input is a leading substring of the scheme. 1758 { "h", "http://www.abc.com", 1759 "http://www.abc.com", 1 }, 1760 { "http", "http://www.abc.com", 1761 "http://www.abc.com", 4 }, 1762 1763 // Inline matches when the input is a leading substring of the full URL. 1764 { "http:", "http://www.abc.com", 1765 "http://www.abc.com", 5 }, 1766 { "http://w", "http://www.abc.com", 1767 "http://www.abc.com", 8 }, 1768 { "http://www.", "http://www.abc.com", 1769 "http://www.abc.com", 11 }, 1770 { "http://www.ab", "http://www.abc.com", 1771 "http://www.abc.com", 13 }, 1772 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1773 "http://www.abc.com/path/file.htm?q=x#foo", 20 }, 1774 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1775 "http://abc.com/path/file.htm?q=x#foo", 16 }, 1776 1777 // Inline matches with valid URLPrefixes; only trim "http://". 1778 { "w", "http://www.abc.com", 1779 "www.abc.com", 1 }, 1780 { "www.a", "http://www.abc.com", 1781 "www.abc.com", 5 }, 1782 { "abc", "http://www.abc.com", 1783 "www.abc.com", 7 }, 1784 { "abc.c", "http://www.abc.com", 1785 "www.abc.com", 9 }, 1786 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1787 "www.abc.com/path/file.htm?q=x#foo", 13 }, 1788 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1789 "abc.com/path/file.htm?q=x#foo", 9 }, 1790 1791 // Inline matches using the maximal URLPrefix components. 1792 { "h", "http://help.com", 1793 "help.com", 1 }, 1794 { "http", "http://http.com", 1795 "http.com", 4 }, 1796 { "h", "http://www.help.com", 1797 "www.help.com", 5 }, 1798 { "http", "http://www.http.com", 1799 "www.http.com", 8 }, 1800 { "w", "http://www.www.com", 1801 "www.www.com", 5 }, 1802 1803 // Test similar behavior for the ftp and https schemes. 1804 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1805 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1806 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1807 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1808 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1809 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1810 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 1811 "ftp://abc.com/path/file.htm?q=x#foo", 8 }, 1812 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1813 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1814 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1815 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1816 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 1817 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1818 { "ab", "https://abc.com/path/file.htm?q=x#foo", 1819 "https://abc.com/path/file.htm?q=x#foo", 10 }, 1820 1821 // Forced query input should inline and retain the "?" prefix. 1822 { "?http://www.ab", "http://www.abc.com", 1823 "?http://www.abc.com", 14 }, 1824 { "?www.ab", "http://www.abc.com", 1825 "?www.abc.com", 7 }, 1826 { "?ab", "http://www.abc.com", 1827 "?www.abc.com", 7 }, 1828 }; 1829 1830 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1831 QueryForInput(ASCIIToUTF16(cases[i].input), false); 1832 SearchProvider::NavigationResult result(GURL(cases[i].url), string16(), 0); 1833 AutocompleteMatch match(provider_->NavigationToMatch(result, false)); 1834 EXPECT_EQ(cases[i].inline_offset, match.inline_autocomplete_offset); 1835 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 1836 } 1837} 1838 1839// Verifies that "http://" is not trimmed for input that is a leading substring. 1840TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 1841 const string16 input(ASCIIToUTF16("ht")); 1842 const string16 url(ASCIIToUTF16("http://a.com")); 1843 const SearchProvider::NavigationResult result(GURL(url), string16(), 0); 1844 1845 // Check the offset and strings when inline autocompletion is allowed. 1846 QueryForInput(input, false); 1847 AutocompleteMatch match_inline(provider_->NavigationToMatch(result, false)); 1848 EXPECT_EQ(2U, match_inline.inline_autocomplete_offset); 1849 EXPECT_EQ(url, match_inline.fill_into_edit); 1850 EXPECT_EQ(url, match_inline.contents); 1851 1852 // Check the same offset and strings when inline autocompletion is prevented. 1853 QueryForInput(input, true); 1854 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result, false)); 1855 EXPECT_EQ(string16::npos, match_prevent.inline_autocomplete_offset); 1856 EXPECT_EQ(url, match_prevent.fill_into_edit); 1857 EXPECT_EQ(url, match_prevent.contents); 1858} 1859 1860// Verifies that input "w" marks a more significant domain label than "www.". 1861TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 1862 QueryForInput(ASCIIToUTF16("w"), false); 1863 const GURL url("http://www.wow.com"); 1864 const SearchProvider::NavigationResult result(url, string16(), 0); 1865 AutocompleteMatch match(provider_->NavigationToMatch(result, false)); 1866 EXPECT_EQ(5U, match.inline_autocomplete_offset); 1867 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 1868 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 1869 1870 // Ensure that the match for input "w" is marked on "wow" and not "www". 1871 ASSERT_EQ(3U, match.contents_class.size()); 1872 EXPECT_EQ(0U, match.contents_class[0].offset); 1873 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1874 match.contents_class[0].style); 1875 EXPECT_EQ(4U, match.contents_class[1].offset); 1876 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 1877 AutocompleteMatch::ACMatchClassification::MATCH, 1878 match.contents_class[1].style); 1879 EXPECT_EQ(5U, match.contents_class[2].offset); 1880 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1881 match.contents_class[2].style); 1882} 1883