search_provider_unittest.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/strings/string_util.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/time.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(AutocompleteMatchType::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 base::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(AutocompleteMatchType::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(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type); 877 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE, 878 result.match_at(1).type); 879 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 880 result.match_at(2).type); 881 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance); 882 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance); 883 884 // The two keyword results should come with the keyword we expect. 885 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword); 886 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword); 887 // The default provider has a different keyword. (We don't explicitly 888 // set it during this test, so all we do is assert that it's different.) 889 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword); 890 891 // The top result will always have a description. The third result, 892 // coming from a different provider than the first two, should also. 893 // Whether the second result has one doesn't matter much. (If it was 894 // missing, people would infer that it's the same search provider as 895 // the one above it.) 896 EXPECT_FALSE(result.match_at(0).description.empty()); 897 EXPECT_FALSE(result.match_at(2).description.empty()); 898 EXPECT_NE(result.match_at(0).description, result.match_at(2).description); 899} 900 901TEST_F(SearchProviderTest, KeywordVerbatim) { 902 TestData cases[] = { 903 // Test a simple keyword input. 904 { ASCIIToUTF16("k foo"), 2, 905 { ResultInfo(GURL("http://keyword/foo"), 906 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 907 ASCIIToUTF16("k foo")), 908 ResultInfo(GURL("http://defaultturl/k%20foo"), 909 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 910 ASCIIToUTF16("k foo") ) } }, 911 912 // Make sure extra whitespace after the keyword doesn't change the 913 // keyword verbatim query. 914 { ASCIIToUTF16("k foo"), 2, 915 { ResultInfo(GURL("http://keyword/foo"), 916 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 917 ASCIIToUTF16("k foo")), 918 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"), 919 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 920 ASCIIToUTF16("k foo")) } }, 921 // Leading whitespace should be stripped before SearchProvider gets the 922 // input; hence there are no tests here about how it handles those inputs. 923 924 // But whitespace elsewhere in the query string should matter to both 925 // matches. 926 { ASCIIToUTF16("k foo bar"), 2, 927 { ResultInfo(GURL("http://keyword/foo%20%20bar"), 928 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 929 ASCIIToUTF16("k foo bar")), 930 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"), 931 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 932 ASCIIToUTF16("k foo bar")) } }, 933 // Note in the above test case we don't test trailing whitespace because 934 // SearchProvider still doesn't handle this well. See related bugs: 935 // 102690, 99239, 164635. 936 937 // Keywords can be prefixed by certain things that should get ignored 938 // when constructing the keyword match. 939 { ASCIIToUTF16("www.k foo"), 2, 940 { ResultInfo(GURL("http://keyword/foo"), 941 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 942 ASCIIToUTF16("k foo")), 943 ResultInfo(GURL("http://defaultturl/www.k%20foo"), 944 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 945 ASCIIToUTF16("www.k foo")) } }, 946 { ASCIIToUTF16("http://k foo"), 2, 947 { ResultInfo(GURL("http://keyword/foo"), 948 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 949 ASCIIToUTF16("k foo")), 950 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"), 951 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 952 ASCIIToUTF16("http://k foo")) } }, 953 { ASCIIToUTF16("http://www.k foo"), 2, 954 { ResultInfo(GURL("http://keyword/foo"), 955 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 956 ASCIIToUTF16("k foo")), 957 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"), 958 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 959 ASCIIToUTF16("http://www.k foo")) } }, 960 961 // A keyword with no remaining input shouldn't get a keyword 962 // verbatim match. 963 { ASCIIToUTF16("k"), 1, 964 { ResultInfo(GURL("http://defaultturl/k"), 965 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 966 ASCIIToUTF16("k")) } }, 967 { ASCIIToUTF16("k "), 1, 968 { ResultInfo(GURL("http://defaultturl/k%20"), 969 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 970 ASCIIToUTF16("k ")) } } 971 972 // The fact that verbatim queries to keyword are handled by KeywordProvider 973 // not SearchProvider is tested in 974 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc. 975 }; 976 977 // Test not in keyword mode. 978 RunTest(cases, arraysize(cases), false); 979 980 // Test in keyword mode. (Both modes should give the same result.) 981 RunTest(cases, arraysize(cases), true); 982} 983 984// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on. 985// Also verifies that just the *first* navigational result is listed as a match 986// if suggested relevance scores were not sent. 987TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) { 988 QueryForInput(ASCIIToUTF16("a.c"), false, false); 989 990 // Make sure the default providers suggest service was queried. 991 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 992 SearchProvider::kDefaultProviderURLFetcherID); 993 ASSERT_TRUE(fetcher); 994 995 // Tell the SearchProvider the suggest query is done. 996 fetcher->set_response_code(200); 997 fetcher->SetResponseString( 998 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 999 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 1000 fetcher->delegate()->OnURLFetchComplete(fetcher); 1001 fetcher = NULL; 1002 1003 // Run till the history results complete. 1004 RunTillProviderDone(); 1005 1006 // Make sure the only match is 'a.com' and it doesn't have a template_url. 1007 AutocompleteMatch nav_match; 1008 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 1009 EXPECT_TRUE(nav_match.keyword.empty()); 1010 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 1011} 1012 1013// Verifies that the most relevant suggest results are added properly. 1014TEST_F(SearchProviderTest, SuggestRelevance) { 1015 QueryForInput(ASCIIToUTF16("a"), false, false); 1016 1017 // Make sure the default provider's suggest service was queried. 1018 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1019 SearchProvider::kDefaultProviderURLFetcherID); 1020 ASSERT_TRUE(fetcher); 1021 1022 // Tell the SearchProvider the suggest query is done. 1023 fetcher->set_response_code(200); 1024 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 1025 fetcher->delegate()->OnURLFetchComplete(fetcher); 1026 fetcher = NULL; 1027 1028 // Run till the history results complete. 1029 RunTillProviderDone(); 1030 1031 // Check the expected verbatim and (first 3) suggestions' relative relevances. 1032 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 1033 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 1034 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 1035 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 1036 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 1037 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 1038 EXPECT_GT(verbatim.relevance, match_a1.relevance); 1039 EXPECT_GT(match_a1.relevance, match_a2.relevance); 1040 EXPECT_GT(match_a2.relevance, match_a3.relevance); 1041} 1042 1043// Verifies that suggest results with relevance scores are added 1044// properly when using the default fetcher. When adding a new test 1045// case to this test, please consider adding it to the tests in 1046// KeywordFetcherSuggestRelevance below. 1047TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) { 1048 struct { 1049 const std::string json; 1050 const std::string matches[4]; 1051 } cases[] = { 1052 // Ensure that suggestrelevance scores reorder matches. 1053 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1054 { "a", "c", "b", kNotApplicable } }, 1055 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1056 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1057 "\"google:suggestrelevance\":[1, 2]}]", 1058 { "a", "c.com", "b.com", kNotApplicable } }, 1059 1060 // Without suggested relevance scores, we should only allow one 1061 // navsuggest result to be be displayed. 1062 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1063 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1064 { "a", "b.com", kNotApplicable, kNotApplicable } }, 1065 1066 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1067 // Negative values will have no effect; the calculated value will be used. 1068 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1069 "\"google:suggestrelevance\":[9998]}]", 1070 { "a", "a1", kNotApplicable, kNotApplicable } }, 1071 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1072 "\"google:suggestrelevance\":[9999]}]", 1073 { "a1", "a", kNotApplicable, kNotApplicable } }, 1074 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1075 "\"google:suggestrelevance\":[9999]}]", 1076 { "a1", kNotApplicable, kNotApplicable, kNotApplicable } }, 1077 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1078 "\"google:suggestrelevance\":[9999]}]", 1079 { "a1", "a", kNotApplicable, kNotApplicable } }, 1080 { "[\"a\",[\"http://a.com\"],[],[]," 1081 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1082 "\"google:verbatimrelevance\":9999," 1083 "\"google:suggestrelevance\":[9998]}]", 1084 { "a", "a.com", kNotApplicable, kNotApplicable } }, 1085 { "[\"a\",[\"http://a.com\"],[],[]," 1086 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1087 "\"google:verbatimrelevance\":9998," 1088 "\"google:suggestrelevance\":[9999]}]", 1089 { "a.com", "a", kNotApplicable, kNotApplicable } }, 1090 { "[\"a\",[\"http://a.com\"],[],[]," 1091 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1092 "\"google:verbatimrelevance\":0," 1093 "\"google:suggestrelevance\":[9999]}]", 1094 { "a.com", kNotApplicable, kNotApplicable, kNotApplicable } }, 1095 { "[\"a\",[\"http://a.com\"],[],[]," 1096 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1097 "\"google:verbatimrelevance\":-1," 1098 "\"google:suggestrelevance\":[9999]}]", 1099 { "a.com", "a", kNotApplicable, kNotApplicable } }, 1100 1101 // Ensure that both types of relevance scores reorder matches together. 1102 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1103 "\"google:verbatimrelevance\":9998}]", 1104 { "a1", "a", "a2", kNotApplicable } }, 1105 1106 // Ensure that only inlinable matches may be ranked as the highest result. 1107 // Ignore all suggested relevance scores if this constraint is violated. 1108 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1109 { "a", "b", kNotApplicable, kNotApplicable } }, 1110 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1111 "\"google:verbatimrelevance\":0}]", 1112 { "a", "b", kNotApplicable, kNotApplicable } }, 1113 { "[\"a\",[\"http://b.com\"],[],[]," 1114 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1115 "\"google:suggestrelevance\":[9999]}]", 1116 { "a", "b.com", kNotApplicable, kNotApplicable } }, 1117 { "[\"a\",[\"http://b.com\"],[],[]," 1118 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1119 "\"google:suggestrelevance\":[9999]," 1120 "\"google:verbatimrelevance\":0}]", 1121 { "a", "b.com", kNotApplicable, kNotApplicable } }, 1122 1123 // Ensure that the top result is ranked as highly as calculated verbatim. 1124 // Ignore the suggested verbatim relevance if this constraint is violated. 1125 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1126 { "a", "a1", kNotApplicable, kNotApplicable } }, 1127 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1128 { "a", "a1", kNotApplicable, kNotApplicable } }, 1129 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1130 "\"google:verbatimrelevance\":0}]", 1131 { "a", "a1", kNotApplicable, kNotApplicable } }, 1132 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1133 "\"google:verbatimrelevance\":0}]", 1134 { "a", "a2", "a1", kNotApplicable } }, 1135 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1136 "\"google:verbatimrelevance\":2}]", 1137 { "a", "a2", "a1", kNotApplicable } }, 1138 { "[\"a\",[\"http://a.com\"],[],[]," 1139 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1140 "\"google:suggestrelevance\":[1]," 1141 "\"google:verbatimrelevance\":0}]", 1142 { "a", "a.com", kNotApplicable, kNotApplicable } }, 1143 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1144 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1145 "\"google:suggestrelevance\":[1, 2]," 1146 "\"google:verbatimrelevance\":0}]", 1147 { "a", "a2.com", "a1.com", kNotApplicable } }, 1148 1149 // Ensure that all suggestions are considered, regardless of order. 1150 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1151 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1152 { "a", "h", "g", "f" } }, 1153 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1154 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1155 "\"http://h.com\"],[],[]," 1156 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1157 "\"NAVIGATION\", \"NAVIGATION\"," 1158 "\"NAVIGATION\", \"NAVIGATION\"," 1159 "\"NAVIGATION\"]," 1160 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1161 { "a", "h.com", "g.com", "f.com" } }, 1162 1163 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1164 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1165 { "a", "a1", "a2", kNotApplicable } }, 1166 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1167 { "a", "a1", kNotApplicable, kNotApplicable } }, 1168 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1169 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1170 "\"google:suggestrelevance\":[1]}]", 1171 { "a", "a1.com", kNotApplicable, kNotApplicable } }, 1172 { "[\"a\",[\"http://a1.com\"],[],[]," 1173 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1174 "\"google:suggestrelevance\":[9999, 1]}]", 1175 { "a", "a1.com", kNotApplicable, kNotApplicable } }, 1176 1177 // Ensure that all 'verbatim' results are merged with their maximum score. 1178 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1179 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1180 { "a2", "a", "a1", kNotApplicable } }, 1181 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1182 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1183 "\"google:verbatimrelevance\":0}]", 1184 { "a2", "a", "a1", kNotApplicable } }, 1185 1186 // Ensure that verbatim is always generated without other suggestions. 1187 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1188 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1189 { "a", kNotApplicable, kNotApplicable, kNotApplicable } }, 1190 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1191 { "a", kNotApplicable, kNotApplicable, kNotApplicable } }, 1192 }; 1193 1194 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1195 QueryForInput(ASCIIToUTF16("a"), false, false); 1196 net::TestURLFetcher* fetcher = WaitUntilURLFetcherIsReady( 1197 SearchProvider::kDefaultProviderURLFetcherID); 1198 ASSERT_TRUE(fetcher); 1199 fetcher->set_response_code(200); 1200 fetcher->SetResponseString(cases[i].json); 1201 fetcher->delegate()->OnURLFetchComplete(fetcher); 1202 RunTillProviderDone(); 1203 1204 const std::string description = "for input with json=" + cases[i].json; 1205 const ACMatches& matches = provider_->matches(); 1206 // The top match must inline and score as highly as calculated verbatim. 1207 EXPECT_NE(string16::npos, matches[0].inline_autocomplete_offset) << 1208 description; 1209 EXPECT_GE(matches[0].relevance, 1300) << description; 1210 1211 size_t j = 0; 1212 // Ensure that the returned matches equal the expectations. 1213 for (; j < matches.size(); ++j) 1214 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1215 matches[j].contents) << description; 1216 // Ensure that no expected matches are missing. 1217 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1218 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 1219 "Case # " << i << " " << description; 1220 } 1221} 1222 1223// Verifies that suggest results with relevance scores are added 1224// properly when using the keyword fetcher. This is similar to the 1225// test DefaultFetcherSuggestRelevance above but this uses inputs that 1226// trigger keyword suggestions (i.e., "k a" rather than "a") and has 1227// different expectations (because now the results are a mix of 1228// keyword suggestions and default provider suggestions). When a new 1229// test is added to this TEST_F, please consider if it would be 1230// appropriate to add to DefaultFetcherSuggestRelevance as well. 1231TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1232 struct { 1233 const std::string json; 1234 const struct { 1235 const std::string contents; 1236 const bool from_keyword; 1237 } matches[5]; 1238 } cases[] = { 1239 // Ensure that suggest relevance scores reorder matches and that 1240 // the keyword verbatim (lacking a suggested verbatim score) beats 1241 // the default provider verbatim. 1242 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1243 { { "a", true }, 1244 { "k a", false }, 1245 { "c", true }, 1246 { "b", true }, 1247 { kNotApplicable, false } } }, 1248 // Again, check that relevance scores reorder matches, just this 1249 // time with navigation matches. This also checks that with 1250 // suggested relevance scores we allow multiple navsuggest results. 1251 // It's odd that navsuggest results that come from a keyword 1252 // provider are marked as not a keyword result. I think this 1253 // comes from them not going to a keyword search engine). 1254 // TODO(mpearson): Investigate the implications (if any) of 1255 // tagging these results appropriately. If so, do it because it 1256 // makes more sense. 1257 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 1258 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1259 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 1260 { { "a", true }, 1261 { "d", true }, 1262 { "c.com", false }, 1263 { "b.com", false }, 1264 { "k a", false }, } }, 1265 1266 // Without suggested relevance scores, we should only allow one 1267 // navsuggest result to be be displayed. 1268 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1269 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1270 { { "a", true }, 1271 { "b.com", false }, 1272 { "k a", false }, 1273 { kNotApplicable, false }, 1274 { kNotApplicable, false } } }, 1275 1276 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1277 // Negative values will have no effect; the calculated value will be used. 1278 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1279 "\"google:suggestrelevance\":[9998]}]", 1280 { { "a", true }, 1281 { "a1", true }, 1282 { "k a", false }, 1283 { kNotApplicable, false }, 1284 { kNotApplicable, false } } }, 1285 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1286 "\"google:suggestrelevance\":[9999]}]", 1287 { { "a1", true }, 1288 { "a", true }, 1289 { "k a", false }, 1290 { kNotApplicable, false }, 1291 { kNotApplicable, false } } }, 1292 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1293 "\"google:suggestrelevance\":[9999]}]", 1294 { { "a1", true }, 1295 { "k a", false }, 1296 { kNotApplicable, false }, 1297 { kNotApplicable, false }, 1298 { kNotApplicable, false } } }, 1299 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1300 "\"google:suggestrelevance\":[9999]}]", 1301 { { "a1", true }, 1302 { "a", true }, 1303 { "k a", false }, 1304 { kNotApplicable, false }, 1305 { kNotApplicable, false } } }, 1306 { "[\"a\",[\"http://a.com\"],[],[]," 1307 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1308 "\"google:verbatimrelevance\":9999," 1309 "\"google:suggestrelevance\":[9998]}]", 1310 { { "a", true }, 1311 { "a.com", false }, 1312 { "k a", false }, 1313 { kNotApplicable, false }, 1314 { kNotApplicable, false } } }, 1315 1316 // Ensure that both types of relevance scores reorder matches together. 1317 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1318 "\"google:verbatimrelevance\":9998}]", 1319 { { "a1", true }, 1320 { "a", true }, 1321 { "a2", true }, 1322 { "k a", false }, 1323 { kNotApplicable, false } } }, 1324 1325 // Ensure that only inlinable matches may be ranked as the highest result. 1326 // Ignore all suggested relevance scores if this constraint is violated. 1327 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1328 { { "a", true }, 1329 { "b", true }, 1330 { "k a", false }, 1331 { kNotApplicable, false }, 1332 { kNotApplicable, false } } }, 1333 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1334 "\"google:verbatimrelevance\":0}]", 1335 { { "a", true }, 1336 { "b", true }, 1337 { "k a", false }, 1338 { kNotApplicable, false }, 1339 { kNotApplicable, false } } }, 1340 { "[\"a\",[\"http://b.com\"],[],[]," 1341 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1342 "\"google:suggestrelevance\":[9999]}]", 1343 { { "a", true }, 1344 { "b.com", false }, 1345 { "k a", false }, 1346 { kNotApplicable, false }, 1347 { kNotApplicable, false } } }, 1348 { "[\"a\",[\"http://b.com\"],[],[]," 1349 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1350 "\"google:suggestrelevance\":[9999]," 1351 "\"google:verbatimrelevance\":0}]", 1352 { { "a", true }, 1353 { "b.com", false }, 1354 { "k a", false }, 1355 { kNotApplicable, false }, 1356 { kNotApplicable, false } } }, 1357 1358 // Ensure that the top result is ranked as highly as calculated verbatim. 1359 // Ignore the suggested verbatim relevance if this constraint is violated. 1360 // Note that keyword suggestions by default (not in suggested relevance 1361 // mode) score more highly than the default verbatim. 1362 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1363 { { "a", true }, 1364 { "a1", true }, 1365 { "k a", false }, 1366 { kNotApplicable, false }, 1367 { kNotApplicable, false } } }, 1368 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1369 { { "a", true }, 1370 { "a1", true }, 1371 { "k a", false }, 1372 { kNotApplicable, false }, 1373 { kNotApplicable, false } } }, 1374 // Continuing the same category of tests, but make sure we keep the 1375 // suggested relevance scores even as we discard the verbatim relevance 1376 // scores. 1377 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1378 "\"google:verbatimrelevance\":0}]", 1379 { { "a", true }, 1380 { "k a", false }, 1381 { "a1", true }, 1382 { kNotApplicable, false }, 1383 { kNotApplicable, false } } }, 1384 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1385 "\"google:verbatimrelevance\":0}]", 1386 { { "a", true }, 1387 { "k a", false }, 1388 { "a2", true }, 1389 { "a1", true }, 1390 { kNotApplicable, false } } }, 1391 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1392 "\"google:verbatimrelevance\":2}]", 1393 { { "a", true }, 1394 { "k a", false }, 1395 { "a2", true }, 1396 { "a1", true }, 1397 { kNotApplicable, false } } }, 1398 1399 // Ensure that all suggestions are considered, regardless of order. 1400 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1401 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1402 { { "a", true }, 1403 { "k a", false }, 1404 { "h", true }, 1405 { "g", true }, 1406 { "f", true } } }, 1407 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1408 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1409 "\"http://h.com\"],[],[]," 1410 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1411 "\"NAVIGATION\", \"NAVIGATION\"," 1412 "\"NAVIGATION\", \"NAVIGATION\"," 1413 "\"NAVIGATION\"]," 1414 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1415 { { "a", true }, 1416 { "k a", false }, 1417 { "h.com", false }, 1418 { "g.com", false }, 1419 { "f.com", false } } }, 1420 1421 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1422 // Note that keyword suggestions by default (not in suggested relevance 1423 // mode) score more highly than the default verbatim. 1424 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1425 { { "a", true }, 1426 { "a1", true }, 1427 { "a2", true }, 1428 { "k a", false }, 1429 { kNotApplicable, false } } }, 1430 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1431 { { "a", true }, 1432 { "a1", true }, 1433 { "k a", false }, 1434 { kNotApplicable, false }, 1435 { kNotApplicable, false } } }, 1436 // In this case, ignored the suggested relevance scores means we keep 1437 // only one navsuggest result. 1438 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1439 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1440 "\"google:suggestrelevance\":[1]}]", 1441 { { "a", true }, 1442 { "a1.com", false }, 1443 { "k a", false }, 1444 { kNotApplicable, false }, 1445 { kNotApplicable, false } } }, 1446 { "[\"a\",[\"http://a1.com\"],[],[]," 1447 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1448 "\"google:suggestrelevance\":[9999, 1]}]", 1449 { { "a", true }, 1450 { "a1.com", false }, 1451 { "k a", false }, 1452 { kNotApplicable, false }, 1453 { kNotApplicable, false } } }, 1454 1455 // Ensure that all 'verbatim' results are merged with their maximum score. 1456 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1457 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1458 { { "a2", true }, 1459 { "a", true }, 1460 { "a1", true }, 1461 { "k a", false }, 1462 { kNotApplicable, false } } }, 1463 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1464 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1465 "\"google:verbatimrelevance\":0}]", 1466 { { "a2", true }, 1467 { "a", true }, 1468 { "a1", true }, 1469 { "k a", false }, 1470 { kNotApplicable, false } } }, 1471 1472 // Ensure that verbatim is always generated without other suggestions. 1473 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1474 // (except when suggested relevances are ignored). 1475 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1476 { { "a", true }, 1477 { "k a", false }, 1478 { kNotApplicable, false }, 1479 { kNotApplicable, false }, 1480 { kNotApplicable, false } } }, 1481 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1482 { { "a", true }, 1483 { "k a", false }, 1484 { kNotApplicable, false }, 1485 { kNotApplicable, false }, 1486 { kNotApplicable, false } } }, 1487 1488 // Check that navsuggestions will be demoted below queries. 1489 // (Navsuggestions are not allowed to appear first.) In the process, 1490 // make sure the navsuggestions still remain in the same order. 1491 // First, check the situation where navsuggest scores more than verbatim 1492 // and there are no query suggestions. 1493 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1494 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1495 "\"google:verbatimrelevance\":9990," 1496 "\"google:suggestrelevance\":[9998, 9999]}]", 1497 { { "a", true }, 1498 { "a2.com", false }, 1499 { "a1.com", false }, 1500 { "k a", false }, 1501 { kNotApplicable, false } } }, 1502 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1503 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1504 "\"google:verbatimrelevance\":9990," 1505 "\"google:suggestrelevance\":[9999, 9998]}]", 1506 { { "a", true }, 1507 { "a1.com", false }, 1508 { "a2.com", false }, 1509 { "k a", false }, 1510 { kNotApplicable, false } } }, 1511 // Check when navsuggest scores more than verbatim and there is query 1512 // suggestion but it scores lower. 1513 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1514 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1515 "\"google:verbatimrelevance\":9990," 1516 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 1517 { { "a", true }, 1518 { "a2.com", false }, 1519 { "a1.com", false }, 1520 { "a3", true }, 1521 { "k a", false } } }, 1522 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1523 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1524 "\"google:verbatimrelevance\":9990," 1525 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 1526 { { "a", true }, 1527 { "a1.com", false }, 1528 { "a2.com", false }, 1529 { "a3", true }, 1530 { "k a", false } } }, 1531 // Check when navsuggest scores more than a query suggestion. There is 1532 // a verbatim but it scores lower. 1533 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1534 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1535 "\"google:verbatimrelevance\":9990," 1536 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1537 { { "a3", true }, 1538 { "a2.com", false }, 1539 { "a1.com", false }, 1540 { "a", true }, 1541 { "k a", false } } }, 1542 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1543 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1544 "\"google:verbatimrelevance\":9990," 1545 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1546 { { "a3", true }, 1547 { "a1.com", false }, 1548 { "a2.com", false }, 1549 { "a", true }, 1550 { "k a", false } } }, 1551 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1552 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1553 "\"google:verbatimrelevance\":0," 1554 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1555 { { "a3", true }, 1556 { "a2.com", false }, 1557 { "a1.com", false }, 1558 { "k a", false }, 1559 { kNotApplicable, false } } }, 1560 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1561 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1562 "\"google:verbatimrelevance\":0," 1563 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1564 { { "a3", true }, 1565 { "a1.com", false }, 1566 { "a2.com", false }, 1567 { "k a", false }, 1568 { kNotApplicable, false } } }, 1569 // Check when there is neither verbatim nor a query suggestion that, 1570 // because we can demote navsuggestions below a query suggestion, 1571 // we abandon suggested relevance scores entirely. One consequence is 1572 // that this means we restore the keyword verbatim match. Note 1573 // that in this case of abandoning suggested relevance scores, we still 1574 // keep the navsuggestions in order by their original scores (just 1575 // not at their original scores), and continue to allow multiple 1576 // navsuggestions to appear. 1577 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1578 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1579 "\"google:verbatimrelevance\":0," 1580 "\"google:suggestrelevance\":[9998, 9999]}]", 1581 { { "a", true }, 1582 { "a2.com", false }, 1583 { "a1.com", false }, 1584 { "k a", false }, 1585 { kNotApplicable, false } } }, 1586 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1587 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1588 "\"google:verbatimrelevance\":0," 1589 "\"google:suggestrelevance\":[9999, 9998]}]", 1590 { { "a", true }, 1591 { "a1.com", false }, 1592 { "a2.com", false }, 1593 { "k a", false }, 1594 { kNotApplicable, false } } }, 1595 // More checks that everything works when it's not necessary to demote. 1596 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1597 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1598 "\"google:verbatimrelevance\":9990," 1599 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 1600 { { "a3", true }, 1601 { "a2.com", false }, 1602 { "a1.com", false }, 1603 { "a", true }, 1604 { "k a", false } } }, 1605 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1606 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1607 "\"google:verbatimrelevance\":9990," 1608 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1609 { { "a3", true }, 1610 { "a1.com", false }, 1611 { "a2.com", false }, 1612 { "a", true }, 1613 { "k a", false } } }, 1614 }; 1615 1616 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1617 QueryForInput(ASCIIToUTF16("k a"), false, true); 1618 1619 // Set up a default fetcher with no results. 1620 net::TestURLFetcher* default_fetcher = WaitUntilURLFetcherIsReady( 1621 SearchProvider::kDefaultProviderURLFetcherID); 1622 ASSERT_TRUE(default_fetcher); 1623 default_fetcher->set_response_code(200); 1624 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1625 default_fetcher = NULL; 1626 1627 // Set up a keyword fetcher with provided results. 1628 net::TestURLFetcher* keyword_fetcher = WaitUntilURLFetcherIsReady( 1629 SearchProvider::kKeywordProviderURLFetcherID); 1630 ASSERT_TRUE(keyword_fetcher); 1631 keyword_fetcher->set_response_code(200); 1632 keyword_fetcher->SetResponseString(cases[i].json); 1633 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1634 keyword_fetcher = NULL; 1635 RunTillProviderDone(); 1636 1637 const std::string description = "for input with json=" + cases[i].json; 1638 const ACMatches& matches = provider_->matches(); 1639 // The top match must inline and score as highly as calculated verbatim. 1640 EXPECT_NE(string16::npos, matches[0].inline_autocomplete_offset) << 1641 description; 1642 EXPECT_GE(matches[0].relevance, 1300) << description; 1643 1644 size_t j = 0; 1645 // Ensure that the returned matches equal the expectations. 1646 for (; j < matches.size(); ++j) { 1647 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1648 matches[j].contents) << description; 1649 EXPECT_EQ(cases[i].matches[j].from_keyword, 1650 matches[j].keyword == ASCIIToUTF16("k")) << description; 1651 } 1652 // Ensure that no expected matches are missing. 1653 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1654 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1655 "Case # " << i << " " << description; 1656 } 1657} 1658 1659// Verifies suggest relevance behavior for URL input. 1660TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 1661 struct { 1662 const std::string input; 1663 const std::string json; 1664 const std::string match_contents[4]; 1665 const AutocompleteMatch::Type match_types[4]; 1666 } cases[] = { 1667 // Ensure topmost NAVIGATION matches are allowed for URL input. 1668 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 1669 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1670 "\"google:suggestrelevance\":[9999]}]", 1671 { "a.com/a", "a.com", kNotApplicable, kNotApplicable }, 1672 { AutocompleteMatchType::NAVSUGGEST, 1673 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1674 AutocompleteMatchType::NUM_TYPES, 1675 AutocompleteMatchType::NUM_TYPES } }, 1676 1677 // Ensure topmost SUGGEST matches are not allowed for URL input. 1678 // SearchProvider disregards search and verbatim suggested relevances. 1679 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 1680 "{\"google:suggestrelevance\":[9999]}]", 1681 { "a.com", "a.com info", kNotApplicable, kNotApplicable }, 1682 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1683 AutocompleteMatchType::SEARCH_SUGGEST, 1684 AutocompleteMatchType::NUM_TYPES, AutocompleteMatchType::NUM_TYPES } }, 1685 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[]," 1686 "{\"google:suggestrelevance\":[9999]}]", 1687 { "a.com", "a.com/a", kNotApplicable, kNotApplicable }, 1688 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1689 AutocompleteMatchType::SEARCH_SUGGEST, 1690 AutocompleteMatchType::NUM_TYPES, AutocompleteMatchType::NUM_TYPES } }, 1691 1692 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 1693 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1694 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1695 "\"google:suggestrelevance\":[9999, 9998]}]", 1696 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 1697 { AutocompleteMatchType::NAVSUGGEST, 1698 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1699 AutocompleteMatchType::SEARCH_SUGGEST, 1700 AutocompleteMatchType::NUM_TYPES } }, 1701 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1702 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1703 "\"google:suggestrelevance\":[9998, 9997]," 1704 "\"google:verbatimrelevance\":9999}]", 1705 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 1706 { AutocompleteMatchType::NAVSUGGEST, 1707 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1708 AutocompleteMatchType::SEARCH_SUGGEST, 1709 AutocompleteMatchType::NUM_TYPES } }, 1710 1711 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches. 1712 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1713 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1714 "\"google:suggestrelevance\":[9999, 9998]}]", 1715 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 1716 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1717 AutocompleteMatchType::NAVSUGGEST, 1718 AutocompleteMatchType::SEARCH_SUGGEST, 1719 AutocompleteMatchType::NUM_TYPES } }, 1720 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1721 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1722 "\"google:suggestrelevance\":[9998, 9997]," 1723 "\"google:verbatimrelevance\":9999}]", 1724 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 1725 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1726 AutocompleteMatchType::NAVSUGGEST, 1727 AutocompleteMatchType::SEARCH_SUGGEST, 1728 AutocompleteMatchType::NUM_TYPES } }, 1729 }; 1730 1731 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1732 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 1733 net::TestURLFetcher* fetcher = WaitUntilURLFetcherIsReady( 1734 SearchProvider::kDefaultProviderURLFetcherID); 1735 ASSERT_TRUE(fetcher); 1736 fetcher->set_response_code(200); 1737 fetcher->SetResponseString(cases[i].json); 1738 fetcher->delegate()->OnURLFetchComplete(fetcher); 1739 RunTillProviderDone(); 1740 1741 size_t j = 0; 1742 const ACMatches& matches = provider_->matches(); 1743 // Ensure that the returned matches equal the expectations. 1744 for (; j < matches.size(); ++j) { 1745 EXPECT_EQ(ASCIIToUTF16(cases[i].match_contents[j]), matches[j].contents); 1746 EXPECT_EQ(cases[i].match_types[j], matches[j].type); 1747 } 1748 // Ensure that no expected matches are missing. 1749 for (; j < ARRAYSIZE_UNSAFE(cases[i].match_contents); ++j) { 1750 EXPECT_EQ(kNotApplicable, cases[i].match_contents[j]); 1751 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, cases[i].match_types[j]); 1752 } 1753 } 1754} 1755 1756// A basic test that verifies the field trial triggered parsing logic. 1757TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 1758 QueryForInput(ASCIIToUTF16("foo"), false, false); 1759 1760 // Make sure the default providers suggest service was queried. 1761 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1762 SearchProvider::kDefaultProviderURLFetcherID); 1763 ASSERT_TRUE(fetcher); 1764 1765 // Tell the SearchProvider the suggest query is done. 1766 fetcher->set_response_code(200); 1767 fetcher->SetResponseString( 1768 "[\"foo\",[\"foo bar\"],[\"\"],[]," 1769 "{\"google:suggesttype\":[\"QUERY\"]," 1770 "\"google:fieldtrialtriggered\":true}]"); 1771 fetcher->delegate()->OnURLFetchComplete(fetcher); 1772 fetcher = NULL; 1773 1774 // Run till the history results complete. 1775 RunTillProviderDone(); 1776 1777 { 1778 // Check for the match and field trial triggered bits. 1779 AutocompleteMatch match; 1780 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 1781 ProvidersInfo providers_info; 1782 provider_->AddProviderInfo(&providers_info); 1783 ASSERT_EQ(1U, providers_info.size()); 1784 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 1785 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 1786 } 1787 { 1788 // Reset the session and check that bits are reset. 1789 provider_->ResetSession(); 1790 ProvidersInfo providers_info; 1791 provider_->AddProviderInfo(&providers_info); 1792 ASSERT_EQ(1U, providers_info.size()); 1793 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 1794 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 1795 } 1796} 1797 1798// Verifies inline autocompletion of navigational results. 1799TEST_F(SearchProviderTest, NavigationInline) { 1800 struct { 1801 const std::string input; 1802 const std::string url; 1803 // Test the expected fill_into_edit, which may drop "http://". 1804 // Some cases do not trim "http://" to match from the start of the scheme. 1805 const std::string fill_into_edit; 1806 size_t inline_offset; 1807 } cases[] = { 1808 // Do not inline matches that do not contain the input; trim http as needed. 1809 { "x", "http://www.abc.com", 1810 "www.abc.com", string16::npos }, 1811 { "https:", "http://www.abc.com", 1812 "www.abc.com", string16::npos }, 1813 { "abc.com/", "http://www.abc.com", 1814 "www.abc.com", string16::npos }, 1815 { "http://www.abc.com/a", "http://www.abc.com", 1816 "http://www.abc.com", string16::npos }, 1817 { "http://www.abc.com", "https://www.abc.com", 1818 "https://www.abc.com", string16::npos }, 1819 { "http://abc.com", "ftp://abc.com", 1820 "ftp://abc.com", string16::npos }, 1821 { "https://www.abc.com", "http://www.abc.com", 1822 "www.abc.com", string16::npos }, 1823 { "ftp://abc.com", "http://abc.com", 1824 "abc.com", string16::npos }, 1825 1826 // Do not inline matches with invalid input prefixes; trim http as needed. 1827 { "ttp", "http://www.abc.com", 1828 "www.abc.com", string16::npos }, 1829 { "://w", "http://www.abc.com", 1830 "www.abc.com", string16::npos }, 1831 { "ww.", "http://www.abc.com", 1832 "www.abc.com", string16::npos }, 1833 { ".ab", "http://www.abc.com", 1834 "www.abc.com", string16::npos }, 1835 { "bc", "http://www.abc.com", 1836 "www.abc.com", string16::npos }, 1837 { ".com", "http://www.abc.com", 1838 "www.abc.com", string16::npos }, 1839 1840 // Do not inline matches that omit input domain labels; trim http as needed. 1841 { "www.a", "http://a.com", 1842 "a.com", string16::npos }, 1843 { "http://www.a", "http://a.com", 1844 "http://a.com", string16::npos }, 1845 { "www.a", "ftp://a.com", 1846 "ftp://a.com", string16::npos }, 1847 { "ftp://www.a", "ftp://a.com", 1848 "ftp://a.com", string16::npos }, 1849 1850 // Input matching but with nothing to inline will not yield an offset. 1851 { "abc.com", "http://www.abc.com", 1852 "www.abc.com", string16::npos }, 1853 { "http://www.abc.com", "http://www.abc.com", 1854 "http://www.abc.com", string16::npos }, 1855 1856 // Inline matches when the input is a leading substring of the scheme. 1857 { "h", "http://www.abc.com", 1858 "http://www.abc.com", 1 }, 1859 { "http", "http://www.abc.com", 1860 "http://www.abc.com", 4 }, 1861 1862 // Inline matches when the input is a leading substring of the full URL. 1863 { "http:", "http://www.abc.com", 1864 "http://www.abc.com", 5 }, 1865 { "http://w", "http://www.abc.com", 1866 "http://www.abc.com", 8 }, 1867 { "http://www.", "http://www.abc.com", 1868 "http://www.abc.com", 11 }, 1869 { "http://www.ab", "http://www.abc.com", 1870 "http://www.abc.com", 13 }, 1871 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1872 "http://www.abc.com/path/file.htm?q=x#foo", 20 }, 1873 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1874 "http://abc.com/path/file.htm?q=x#foo", 16 }, 1875 1876 // Inline matches with valid URLPrefixes; only trim "http://". 1877 { "w", "http://www.abc.com", 1878 "www.abc.com", 1 }, 1879 { "www.a", "http://www.abc.com", 1880 "www.abc.com", 5 }, 1881 { "abc", "http://www.abc.com", 1882 "www.abc.com", 7 }, 1883 { "abc.c", "http://www.abc.com", 1884 "www.abc.com", 9 }, 1885 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1886 "www.abc.com/path/file.htm?q=x#foo", 13 }, 1887 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1888 "abc.com/path/file.htm?q=x#foo", 9 }, 1889 1890 // Inline matches using the maximal URLPrefix components. 1891 { "h", "http://help.com", 1892 "help.com", 1 }, 1893 { "http", "http://http.com", 1894 "http.com", 4 }, 1895 { "h", "http://www.help.com", 1896 "www.help.com", 5 }, 1897 { "http", "http://www.http.com", 1898 "www.http.com", 8 }, 1899 { "w", "http://www.www.com", 1900 "www.www.com", 5 }, 1901 1902 // Test similar behavior for the ftp and https schemes. 1903 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1904 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1905 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1906 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1907 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1908 "ftp://www.abc.com/path/file.htm?q=x#foo", 12 }, 1909 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 1910 "ftp://abc.com/path/file.htm?q=x#foo", 8 }, 1911 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1912 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1913 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1914 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1915 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 1916 "https://www.abc.com/path/file.htm?q=x#foo", 14 }, 1917 { "ab", "https://abc.com/path/file.htm?q=x#foo", 1918 "https://abc.com/path/file.htm?q=x#foo", 10 }, 1919 1920 // Forced query input should inline and retain the "?" prefix. 1921 { "?http://www.ab", "http://www.abc.com", 1922 "?http://www.abc.com", 14 }, 1923 { "?www.ab", "http://www.abc.com", 1924 "?www.abc.com", 7 }, 1925 { "?ab", "http://www.abc.com", 1926 "?www.abc.com", 7 }, 1927 }; 1928 1929 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1930 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 1931 SearchProvider::NavigationResult result( 1932 *provider_.get(), GURL(cases[i].url), string16(), false, 0); 1933 AutocompleteMatch match(provider_->NavigationToMatch(result)); 1934 EXPECT_EQ(cases[i].inline_offset, match.inline_autocomplete_offset); 1935 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 1936 } 1937} 1938 1939// Verifies that "http://" is not trimmed for input that is a leading substring. 1940TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 1941 const string16 input(ASCIIToUTF16("ht")); 1942 const string16 url(ASCIIToUTF16("http://a.com")); 1943 const SearchProvider::NavigationResult result( 1944 *provider_.get(), GURL(url), string16(), false, 0); 1945 1946 // Check the offset and strings when inline autocompletion is allowed. 1947 QueryForInput(input, false, false); 1948 AutocompleteMatch match_inline(provider_->NavigationToMatch(result)); 1949 EXPECT_EQ(2U, match_inline.inline_autocomplete_offset); 1950 EXPECT_EQ(url, match_inline.fill_into_edit); 1951 EXPECT_EQ(url, match_inline.contents); 1952 1953 // Check the same offset and strings when inline autocompletion is prevented. 1954 QueryForInput(input, true, false); 1955 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result)); 1956 EXPECT_EQ(string16::npos, match_prevent.inline_autocomplete_offset); 1957 EXPECT_EQ(url, match_prevent.fill_into_edit); 1958 EXPECT_EQ(url, match_prevent.contents); 1959} 1960 1961// Verifies that input "w" marks a more significant domain label than "www.". 1962TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 1963 QueryForInput(ASCIIToUTF16("w"), false, false); 1964 const GURL url("http://www.wow.com"); 1965 const SearchProvider::NavigationResult result( 1966 *provider_.get(), url, string16(), false, 0); 1967 AutocompleteMatch match(provider_->NavigationToMatch(result)); 1968 EXPECT_EQ(5U, match.inline_autocomplete_offset); 1969 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 1970 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 1971 1972 // Ensure that the match for input "w" is marked on "wow" and not "www". 1973 ASSERT_EQ(3U, match.contents_class.size()); 1974 EXPECT_EQ(0U, match.contents_class[0].offset); 1975 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1976 match.contents_class[0].style); 1977 EXPECT_EQ(4U, match.contents_class[1].offset); 1978 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 1979 AutocompleteMatch::ACMatchClassification::MATCH, 1980 match.contents_class[1].style); 1981 EXPECT_EQ(5U, match.contents_class[2].offset); 1982 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1983 match.contents_class[2].style); 1984} 1985 1986TEST_F(SearchProviderTest, RemoveStaleResultsTest) { 1987 // TODO(mpearson): Consider expanding this test to explicitly cover 1988 // testing staleness for keyword results. 1989 struct { 1990 const std::string omnibox_input; 1991 const int verbatim_relevance; 1992 // These cached suggestions should already be sorted. 1993 // The particular number 5 as the length of the array is 1994 // unimportant; it's merely enough cached results to fully test 1995 // the functioning of RemoveAllStaleResults(). 1996 struct { 1997 const std::string suggestion; 1998 const bool is_navigation_result; 1999 const int relevance; 2000 // |expect_match| is true if this result should survive 2001 // RemoveAllStaleResults() filtering against |omnibox_input| below. 2002 const bool expect_match; 2003 } results[5]; 2004 } cases[] = { 2005 // Simple case: multiple query suggestions and no navsuggestions. 2006 // All query suggestions score less than search-what-you-typed and 2007 // thus none should be filtered because none will appear first. 2008 { "x", 1300, 2009 { { "food", false, 1299, true }, 2010 { "foobar", false, 1298, true }, 2011 { "crazy", false, 1297, true }, 2012 { "friend", false, 1296, true }, 2013 { kNotApplicable, false, 0, false } } }, 2014 2015 // Similarly simple cases, but the query suggestion appears first. 2016 { "f", 1200, 2017 { { "food", false, 1299, true }, 2018 { "foobar", false, 1298, true }, 2019 { "crazy", false, 1297, true }, 2020 { "friend", false, 1296, true }, 2021 { kNotApplicable, false, 0, false } } }, 2022 { "c", 1200, 2023 { { "food", false, 1299, false }, 2024 { "foobar", false, 1298, false }, 2025 { "crazy", false, 1297, true }, 2026 { "friend", false, 1296, true }, 2027 { kNotApplicable, false, 0, false } } }, 2028 { "x", 1200, 2029 { { "food", false, 1299, false }, 2030 { "foobar", false, 1298, false }, 2031 { "crazy", false, 1297, false }, 2032 { "friend", false, 1296, false }, 2033 { kNotApplicable, false, 0, false } } }, 2034 2035 // The same sort of cases, just using a mix of queries and navsuggestions. 2036 { "x", 1300, 2037 { { "http://food.com/", true, 1299, true }, 2038 { "foobar", false, 1298, true }, 2039 { "http://crazy.com/", true, 1297, true }, 2040 { "friend", false, 1296, true }, 2041 { "http://friend.com/", true, 1295, true } } }, 2042 { "f", 1200, 2043 { { "http://food.com/", true, 1299, true }, 2044 { "foobar", false, 1298, true }, 2045 { "http://crazy.com/", true, 1297, true }, 2046 { "friend", false, 1296, true }, 2047 { "http://friend.com/", true, 1295, true } } }, 2048 { "c", 1200, 2049 { { "http://food.com/", true, 1299, false }, 2050 { "foobar", false, 1298, false }, 2051 { "http://crazy.com/", true, 1297, true }, 2052 { "friend", false, 1296, true }, 2053 { "http://friend.com/", true, 1295, true } } }, 2054 { "x", 1200, 2055 { { "http://food.com/", true, 1299, false }, 2056 { "foobar", false, 1298, false }, 2057 { "http://crazy.com/", true, 1297, false }, 2058 { "friend", false, 1296, false }, 2059 { "http://friend.com/", true, 1295, false } } }, 2060 2061 // Run the three tests immediately above again, just with verbatim 2062 // suppressed. Note that in the last case, all results are filtered. 2063 // Because verbatim is also suppressed, SearchProvider will realize 2064 // in UpdateMatches() that it needs to restore verbatim to fulfill 2065 // its constraints. This restoration does not happen in 2066 // RemoveAllStaleResults() and hence is not tested here. This restoration 2067 // is tested in the DefaultFetcherSuggestRelevance test. 2068 { "f", 0, 2069 { { "http://food.com/", true, 1299, true }, 2070 { "foobar", false, 1298, true }, 2071 { "http://crazy.com/", true, 1297, true }, 2072 { "friend", false, 1296, true }, 2073 { "http://friend.com/", true, 1295, true } } }, 2074 { "c", 0, 2075 { { "http://food.com/", true, 1299, false }, 2076 { "foobar", false, 1298, false }, 2077 { "http://crazy.com/", true, 1297, true }, 2078 { "friend", false, 1296, true }, 2079 { "http://friend.com/", true, 1295, true } } }, 2080 { "x", 0, 2081 { { "http://food.com/", true, 1299, false }, 2082 { "foobar", false, 1298, false }, 2083 { "http://crazy.com/", true, 1297, false }, 2084 { "friend", false, 1296, false }, 2085 { "http://friend.com/", true, 1295, false } } }, 2086 2087 // The same sort of tests again, just with verbatim with a score 2088 // that would place it in between other suggestions. 2089 { "f", 1290, 2090 { { "http://food.com/", true, 1299, true }, 2091 { "foobar", false, 1288, true }, 2092 { "http://crazy.com/", true, 1277, true }, 2093 { "friend", false, 1266, true }, 2094 { "http://friend.com/", true, 1255, true } } }, 2095 { "c", 1290, 2096 { { "http://food.com/", true, 1299, false }, 2097 { "foobar", false, 1288, true }, 2098 { "http://crazy.com/", true, 1277, true }, 2099 { "friend", false, 1266, true }, 2100 { "http://friend.com/", true, 1255, true } } }, 2101 { "c", 1270, 2102 { { "http://food.com/", true, 1299, false }, 2103 { "foobar", false, 1288, false }, 2104 { "http://crazy.com/", true, 1277, true }, 2105 { "friend", false, 1266, true }, 2106 { "http://friend.com/", true, 1255, true } } }, 2107 { "x", 1280, 2108 { { "http://food.com/", true, 1299, false }, 2109 { "foobar", false, 1288, false }, 2110 { "http://crazy.com/", true, 1277, true }, 2111 { "friend", false, 1266, true }, 2112 { "http://friend.com/", true, 1255, true } } }, 2113 }; 2114 2115 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2116 // Initialize cached results for this test case. 2117 provider_->default_results_.verbatim_relevance = 2118 cases[i].verbatim_relevance; 2119 provider_->default_results_.navigation_results.clear(); 2120 provider_->default_results_.suggest_results.clear(); 2121 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2122 const std::string& suggestion = cases[i].results[j].suggestion; 2123 if (suggestion == kNotApplicable) 2124 break; 2125 if (cases[i].results[j].is_navigation_result) { 2126 provider_->default_results_.navigation_results.push_back( 2127 SearchProvider::NavigationResult(*provider_.get(), 2128 GURL(suggestion), 2129 string16(), 2130 false, 2131 cases[i].results[j].relevance)); 2132 } else { 2133 provider_->default_results_.suggest_results.push_back( 2134 SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false, 2135 cases[i].results[j].relevance)); 2136 } 2137 } 2138 2139 provider_->input_ = AutocompleteInput( 2140 ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(), 2141 GURL(), false, false, true, AutocompleteInput::ALL_MATCHES); 2142 provider_->RemoveAllStaleResults(); 2143 2144 // Check cached results. 2145 SearchProvider::SuggestResults::const_iterator sug_it = 2146 provider_->default_results_.suggest_results.begin(); 2147 const SearchProvider::SuggestResults::const_iterator sug_end = 2148 provider_->default_results_.suggest_results.end(); 2149 SearchProvider::NavigationResults::const_iterator nav_it = 2150 provider_->default_results_.navigation_results.begin(); 2151 const SearchProvider::NavigationResults::const_iterator nav_end = 2152 provider_->default_results_.navigation_results.end(); 2153 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2154 const std::string& suggestion = cases[i].results[j].suggestion; 2155 if (suggestion == kNotApplicable) 2156 continue; 2157 if (!cases[i].results[j].expect_match) 2158 continue; 2159 if (cases[i].results[j].is_navigation_result) { 2160 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion; 2161 EXPECT_EQ(suggestion, nav_it->url().spec()); 2162 ++nav_it; 2163 } else { 2164 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion; 2165 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion()); 2166 ++sug_it; 2167 } 2168 } 2169 EXPECT_EQ(sug_end, sug_it); 2170 EXPECT_EQ(nav_end, nav_it); 2171 } 2172} 2173