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