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