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