search_provider_unittest.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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 <string> 8 9#include "base/command_line.h" 10#include "base/metrics/field_trial.h" 11#include "base/prefs/pref_service.h" 12#include "base/run_loop.h" 13#include "base/strings/string16.h" 14#include "base/strings/string_number_conversions.h" 15#include "base/strings/string_util.h" 16#include "base/strings/utf_string_conversions.h" 17#include "base/time/time.h" 18#include "build/build_config.h" 19#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 20#include "chrome/browser/autocomplete/autocomplete_controller.h" 21#include "chrome/browser/autocomplete/autocomplete_input.h" 22#include "chrome/browser/autocomplete/autocomplete_match.h" 23#include "chrome/browser/autocomplete/autocomplete_provider.h" 24#include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 25#include "chrome/browser/autocomplete/history_url_provider.h" 26#include "chrome/browser/history/history_service.h" 27#include "chrome/browser/history/history_service_factory.h" 28#include "chrome/browser/omnibox/omnibox_field_trial.h" 29#include "chrome/browser/search_engines/template_url.h" 30#include "chrome/browser/search_engines/template_url_service.h" 31#include "chrome/browser/search_engines/template_url_service_factory.h" 32#include "chrome/browser/signin/signin_manager_factory.h" 33#include "chrome/browser/sync/profile_sync_service.h" 34#include "chrome/browser/sync/profile_sync_service_factory.h" 35#include "chrome/common/chrome_switches.h" 36#include "chrome/common/pref_names.h" 37#include "chrome/test/base/testing_browser_process.h" 38#include "chrome/test/base/testing_profile.h" 39#include "components/google/core/browser/google_switches.h" 40#include "components/search_engines/search_engine_type.h" 41#include "components/signin/core/browser/signin_manager.h" 42#include "components/sync_driver/pref_names.h" 43#include "components/variations/entropy_provider.h" 44#include "components/variations/variations_associated_data.h" 45#include "content/public/test/test_browser_thread_bundle.h" 46#include "net/url_request/test_url_fetcher_factory.h" 47#include "net/url_request/url_request_status.h" 48#include "testing/gtest/include/gtest/gtest.h" 49 50using base::ASCIIToUTF16; 51 52namespace { 53 54// Returns the first match in |matches| with |allowed_to_be_default_match| 55// set to true. 56ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) { 57 ACMatches::const_iterator it = matches.begin(); 58 while ((it != matches.end()) && !it->allowed_to_be_default_match) 59 ++it; 60 return it; 61} 62 63class SuggestionDeletionHandler; 64class SearchProviderForTest : public SearchProvider { 65 public: 66 SearchProviderForTest( 67 AutocompleteProviderListener* listener, 68 Profile* profile); 69 bool is_success() { return is_success_; }; 70 71 protected: 72 virtual ~SearchProviderForTest(); 73 74 private: 75 virtual void RecordDeletionResult(bool success) OVERRIDE; 76 bool is_success_; 77 DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest); 78}; 79 80SearchProviderForTest::SearchProviderForTest( 81 AutocompleteProviderListener* listener, 82 Profile* profile) 83 : SearchProvider(listener, profile), is_success_(false) { 84} 85 86SearchProviderForTest::~SearchProviderForTest() { 87} 88 89void SearchProviderForTest::RecordDeletionResult(bool success) { 90 is_success_ = success; 91} 92 93} // namespace 94 95// SearchProviderTest --------------------------------------------------------- 96 97// The following environment is configured for these tests: 98// . The TemplateURL default_t_url_ is set as the default provider. 99// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This 100// TemplateURL has a valid suggest and search URL. 101// . The URL created by using the search term term1_ with default_t_url_ is 102// added to history. 103// . The URL created by using the search term keyword_term_ with keyword_t_url_ 104// is added to history. 105// . test_factory_ is set as the URLFetcherFactory. 106class SearchProviderTest : public testing::Test, 107 public AutocompleteProviderListener { 108 public: 109 struct ResultInfo { 110 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES), 111 allowed_to_be_default_match(false) { 112 } 113 ResultInfo(GURL gurl, 114 AutocompleteMatch::Type result_type, 115 bool allowed_to_be_default_match, 116 base::string16 fill_into_edit) 117 : gurl(gurl), 118 result_type(result_type), 119 allowed_to_be_default_match(allowed_to_be_default_match), 120 fill_into_edit(fill_into_edit) { 121 } 122 123 const GURL gurl; 124 const AutocompleteMatch::Type result_type; 125 const bool allowed_to_be_default_match; 126 const base::string16 fill_into_edit; 127 }; 128 129 struct TestData { 130 const base::string16 input; 131 const size_t num_results; 132 const ResultInfo output[3]; 133 }; 134 135 SearchProviderTest() 136 : default_t_url_(NULL), 137 term1_(ASCIIToUTF16("term1")), 138 keyword_t_url_(NULL), 139 keyword_term_(ASCIIToUTF16("keyword")), 140 run_loop_(NULL) { 141 ResetFieldTrialList(); 142 } 143 144 // See description above class for what this registers. 145 virtual void SetUp() OVERRIDE; 146 virtual void TearDown() OVERRIDE; 147 148 void RunTest(TestData* cases, int num_cases, bool prefer_keyword); 149 150 protected: 151 // Needed for AutocompleteFieldTrial::ActivateStaticTrials(); 152 scoped_ptr<base::FieldTrialList> field_trial_list_; 153 154 // Default value used for testing. 155 static const std::string kNotApplicable; 156 157 // Adds a search for |term|, using the engine |t_url| to the history, and 158 // returns the URL for that search. 159 GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count); 160 161 // Looks for a match in |provider_| with |contents| equal to |contents|. 162 // Sets |match| to it if found. Returns whether |match| was set. 163 bool FindMatchWithContents(const base::string16& contents, 164 AutocompleteMatch* match); 165 166 // Looks for a match in |provider_| with destination |url|. Sets |match| to 167 // it if found. Returns whether |match| was set. 168 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match); 169 170 // AutocompleteProviderListener: 171 // If we're waiting for the provider to finish, this exits the message loop. 172 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 173 174 // Runs a nested message loop until provider_ is done. The message loop is 175 // exited by way of OnProviderUpdate. 176 void RunTillProviderDone(); 177 178 // Invokes Start on provider_, then runs all pending tasks. 179 void QueryForInput(const base::string16& text, 180 bool prevent_inline_autocomplete, 181 bool prefer_keyword); 182 183 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is 184 // non-NULL, sets it to the "what you typed" entry for |text|. 185 void QueryForInputAndSetWYTMatch(const base::string16& text, 186 AutocompleteMatch* wyt_match); 187 188 // Notifies the URLFetcher for the suggest query corresponding to the default 189 // search provider that it's done. 190 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE. 191 void FinishDefaultSuggestQuery(); 192 193 // Runs SearchProvider on |input|, for which the suggest server replies 194 // with |json|, and expects that the resulting matches' contents equals 195 // that in |matches|. An empty entry in |matches| means no match should 196 // be returned in that position. Reports any errors with a message that 197 // includes |error_description|. 198 void ForcedQueryTestHelper(const std::string& input, 199 const std::string& json, 200 const std::string matches[3], 201 const std::string& error_description); 202 203 void ResetFieldTrialList(); 204 205 void ClearAllResults(); 206 207 // See description above class for details of these fields. 208 TemplateURL* default_t_url_; 209 const base::string16 term1_; 210 GURL term1_url_; 211 TemplateURL* keyword_t_url_; 212 const base::string16 keyword_term_; 213 GURL keyword_url_; 214 215 content::TestBrowserThreadBundle thread_bundle_; 216 217 // URLFetcherFactory implementation registered. 218 net::TestURLFetcherFactory test_factory_; 219 220 // Profile we use. 221 TestingProfile profile_; 222 223 // The provider. 224 scoped_refptr<SearchProviderForTest> provider_; 225 226 // If non-NULL, OnProviderUpdate quits the current |run_loop_|. 227 base::RunLoop* run_loop_; 228 229 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest); 230}; 231 232// static 233const std::string SearchProviderTest::kNotApplicable = "Not Applicable"; 234 235void SearchProviderTest::SetUp() { 236 // Make sure that fetchers are automatically ungregistered upon destruction. 237 test_factory_.set_remove_fetcher_on_delete(true); 238 239 // We need both the history service and template url model loaded. 240 ASSERT_TRUE(profile_.CreateHistoryService(true, false)); 241 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 242 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 243 244 TemplateURLService* turl_model = 245 TemplateURLServiceFactory::GetForProfile(&profile_); 246 247 turl_model->Load(); 248 249 // Reset the default TemplateURL. 250 TemplateURLData data; 251 data.short_name = ASCIIToUTF16("t"); 252 data.SetURL("http://defaultturl/{searchTerms}"); 253 data.suggestions_url = "http://defaultturl2/{searchTerms}"; 254 data.instant_url = "http://does/not/exist?strk=1"; 255 data.search_terms_replacement_key = "strk"; 256 default_t_url_ = new TemplateURL(data); 257 turl_model->Add(default_t_url_); 258 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_); 259 TemplateURLID default_provider_id = default_t_url_->id(); 260 ASSERT_NE(0, default_provider_id); 261 262 // Add url1, with search term term1_. 263 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1); 264 265 // Create another TemplateURL. 266 data.short_name = ASCIIToUTF16("k"); 267 data.SetKeyword(ASCIIToUTF16("k")); 268 data.SetURL("http://keyword/{searchTerms}"); 269 data.suggestions_url = "http://suggest_keyword/{searchTerms}"; 270 keyword_t_url_ = new TemplateURL(data); 271 turl_model->Add(keyword_t_url_); 272 ASSERT_NE(0, keyword_t_url_->id()); 273 274 // Add a page and search term for keyword_t_url_. 275 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1); 276 277 // Keywords are updated by the InMemoryHistoryBackend only after the message 278 // has been processed on the history thread. Block until history processes all 279 // requests to ensure the InMemoryDatabase is the state we expect it. 280 profile_.BlockUntilHistoryProcessesPendingRequests(); 281 282 provider_ = new SearchProviderForTest(this, &profile_); 283 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0; 284} 285 286void SearchProviderTest::TearDown() { 287 base::RunLoop().RunUntilIdle(); 288 289 // Shutdown the provider before the profile. 290 provider_ = NULL; 291} 292 293void SearchProviderTest::RunTest(TestData* cases, 294 int num_cases, 295 bool prefer_keyword) { 296 ACMatches matches; 297 for (int i = 0; i < num_cases; ++i) { 298 AutocompleteInput input(cases[i].input, base::string16::npos, 299 base::string16(), GURL(), 300 AutocompleteInput::INVALID_SPEC, false, 301 prefer_keyword, true, true); 302 provider_->Start(input, false); 303 matches = provider_->matches(); 304 base::string16 diagnostic_details = 305 ASCIIToUTF16("Input was: ") + 306 cases[i].input + 307 ASCIIToUTF16("; prefer_keyword was: ") + 308 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")); 309 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details; 310 if (matches.size() == cases[i].num_results) { 311 for (size_t j = 0; j < cases[i].num_results; ++j) { 312 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) << 313 diagnostic_details; 314 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) << 315 diagnostic_details; 316 EXPECT_EQ(cases[i].output[j].fill_into_edit, 317 matches[j].fill_into_edit) << diagnostic_details; 318 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 319 matches[j].allowed_to_be_default_match) << diagnostic_details; 320 } 321 } 322 } 323} 324 325void SearchProviderTest::OnProviderUpdate(bool updated_matches) { 326 if (run_loop_ && provider_->done()) { 327 run_loop_->Quit(); 328 run_loop_ = NULL; 329 } 330} 331 332void SearchProviderTest::RunTillProviderDone() { 333 if (provider_->done()) 334 return; 335 336 base::RunLoop run_loop; 337 run_loop_ = &run_loop; 338 run_loop.Run(); 339} 340 341void SearchProviderTest::QueryForInput(const base::string16& text, 342 bool prevent_inline_autocomplete, 343 bool prefer_keyword) { 344 // Start a query. 345 AutocompleteInput input(text, base::string16::npos, base::string16(), GURL(), 346 AutocompleteInput::INVALID_SPEC, 347 prevent_inline_autocomplete, prefer_keyword, true, 348 true); 349 provider_->Start(input, false); 350 351 // RunUntilIdle so that the task scheduled by SearchProvider to create the 352 // URLFetchers runs. 353 base::RunLoop().RunUntilIdle(); 354} 355 356void SearchProviderTest::QueryForInputAndSetWYTMatch( 357 const base::string16& text, 358 AutocompleteMatch* wyt_match) { 359 QueryForInput(text, false, false); 360 profile_.BlockUntilHistoryProcessesPendingRequests(); 361 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 362 if (!wyt_match) 363 return; 364 ASSERT_GE(provider_->matches().size(), 1u); 365 EXPECT_TRUE(FindMatchWithDestination( 366 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 367 TemplateURLRef::SearchTermsArgs(base::CollapseWhitespace( 368 text, false)), 369 TemplateURLServiceFactory::GetForProfile( 370 &profile_)->search_terms_data())), 371 wyt_match)); 372} 373 374GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url, 375 base::string16 term, 376 int visit_count) { 377 HistoryService* history = 378 HistoryServiceFactory::GetForProfile(&profile_, 379 Profile::EXPLICIT_ACCESS); 380 GURL search(t_url->url_ref().ReplaceSearchTerms( 381 TemplateURLRef::SearchTermsArgs(term), 382 TemplateURLServiceFactory::GetForProfile( 383 &profile_)->search_terms_data())); 384 static base::Time last_added_time; 385 last_added_time = std::max(base::Time::Now(), 386 last_added_time + base::TimeDelta::FromMicroseconds(1)); 387 history->AddPageWithDetails(search, base::string16(), visit_count, visit_count, 388 last_added_time, false, history::SOURCE_BROWSED); 389 history->SetKeywordSearchTermsForURL(search, t_url->id(), term); 390 return search; 391} 392 393bool SearchProviderTest::FindMatchWithContents(const base::string16& contents, 394 AutocompleteMatch* match) { 395 for (ACMatches::const_iterator i = provider_->matches().begin(); 396 i != provider_->matches().end(); ++i) { 397 if (i->contents == contents) { 398 *match = *i; 399 return true; 400 } 401 } 402 return false; 403} 404 405bool SearchProviderTest::FindMatchWithDestination(const GURL& url, 406 AutocompleteMatch* match) { 407 for (ACMatches::const_iterator i = provider_->matches().begin(); 408 i != provider_->matches().end(); ++i) { 409 if (i->destination_url == url) { 410 *match = *i; 411 return true; 412 } 413 } 414 return false; 415} 416 417void SearchProviderTest::FinishDefaultSuggestQuery() { 418 net::TestURLFetcher* default_fetcher = 419 test_factory_.GetFetcherByID( 420 SearchProvider::kDefaultProviderURLFetcherID); 421 ASSERT_TRUE(default_fetcher); 422 423 // Tell the SearchProvider the default suggest query is done. 424 default_fetcher->set_response_code(200); 425 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 426} 427 428void SearchProviderTest::ForcedQueryTestHelper( 429 const std::string& input, 430 const std::string& json, 431 const std::string expected_matches[3], 432 const std::string& error_description) { 433 QueryForInput(ASCIIToUTF16(input), false, false); 434 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 435 SearchProvider::kDefaultProviderURLFetcherID); 436 ASSERT_TRUE(fetcher); 437 fetcher->set_response_code(200); 438 fetcher->SetResponseString(json); 439 fetcher->delegate()->OnURLFetchComplete(fetcher); 440 RunTillProviderDone(); 441 442 const ACMatches& matches = provider_->matches(); 443 ASSERT_LE(matches.size(), 3u); 444 size_t i = 0; 445 // Ensure that the returned matches equal the expectations. 446 for (; i < matches.size(); ++i) { 447 EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) << 448 error_description; 449 } 450 // Ensure that no expected matches are missing. 451 for (; i < 3u; ++i) { 452 EXPECT_EQ(std::string(), expected_matches[i]) << 453 "Case #" << i << ": " << error_description; 454 } 455} 456 457void SearchProviderTest::ResetFieldTrialList() { 458 // Destroy the existing FieldTrialList before creating a new one to avoid 459 // a DCHECK. 460 field_trial_list_.reset(); 461 field_trial_list_.reset(new base::FieldTrialList( 462 new metrics::SHA1EntropyProvider("foo"))); 463 chrome_variations::testing::ClearAllVariationParams(); 464 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial( 465 "AutocompleteDynamicTrial_0", "DefaultGroup"); 466 trial->group(); 467} 468 469void SearchProviderTest::ClearAllResults() { 470 provider_->ClearAllResults(); 471} 472 473// Actual Tests --------------------------------------------------------------- 474 475// Make sure we query history for the default provider and a URLFetcher is 476// created for the default provider suggest results. 477TEST_F(SearchProviderTest, QueryDefaultProvider) { 478 base::string16 term = term1_.substr(0, term1_.length() - 1); 479 QueryForInput(term, false, false); 480 481 // Make sure the default providers suggest service was queried. 482 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 483 SearchProvider::kDefaultProviderURLFetcherID); 484 ASSERT_TRUE(fetcher); 485 486 // And the URL matches what we expected. 487 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 488 TemplateURLRef::SearchTermsArgs(term), 489 TemplateURLServiceFactory::GetForProfile( 490 &profile_)->search_terms_data())); 491 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url); 492 493 // Tell the SearchProvider the suggest query is done. 494 fetcher->set_response_code(200); 495 fetcher->delegate()->OnURLFetchComplete(fetcher); 496 fetcher = NULL; 497 498 // Run till the history results complete. 499 RunTillProviderDone(); 500 501 // The SearchProvider is done. Make sure it has a result for the history 502 // term term1. 503 AutocompleteMatch term1_match; 504 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match)); 505 // Term1 should not have a description, it's set later. 506 EXPECT_TRUE(term1_match.description.empty()); 507 508 AutocompleteMatch wyt_match; 509 EXPECT_TRUE(FindMatchWithDestination( 510 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 511 TemplateURLRef::SearchTermsArgs(term), 512 TemplateURLServiceFactory::GetForProfile( 513 &profile_)->search_terms_data())), 514 &wyt_match)); 515 EXPECT_TRUE(wyt_match.description.empty()); 516 517 // The match for term1 should be more relevant than the what you typed match. 518 EXPECT_GT(term1_match.relevance, wyt_match.relevance); 519 // This longer match should be inlineable. 520 EXPECT_TRUE(term1_match.allowed_to_be_default_match); 521 // The what you typed match should be too, of course. 522 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 523} 524 525TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) { 526 base::string16 term = term1_.substr(0, term1_.length() - 1); 527 QueryForInput(term, true, false); 528 529 ASSERT_FALSE(provider_->matches().empty()); 530 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 531 provider_->matches()[0].type); 532 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match); 533} 534 535// Issues a query that matches the registered keyword and makes sure history 536// is queried as well as URLFetchers getting created. 537TEST_F(SearchProviderTest, QueryKeywordProvider) { 538 base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1); 539 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term, 540 false, 541 false); 542 543 // Make sure the default providers suggest service was queried. 544 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 545 SearchProvider::kDefaultProviderURLFetcherID); 546 ASSERT_TRUE(default_fetcher); 547 548 // Tell the SearchProvider the default suggest query is done. 549 default_fetcher->set_response_code(200); 550 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 551 default_fetcher = NULL; 552 553 // Make sure the keyword providers suggest service was queried. 554 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID( 555 SearchProvider::kKeywordProviderURLFetcherID); 556 ASSERT_TRUE(keyword_fetcher); 557 558 // And the URL matches what we expected. 559 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms( 560 TemplateURLRef::SearchTermsArgs(term), 561 TemplateURLServiceFactory::GetForProfile( 562 &profile_)->search_terms_data())); 563 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url); 564 565 // Tell the SearchProvider the keyword suggest query is done. 566 keyword_fetcher->set_response_code(200); 567 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 568 keyword_fetcher = NULL; 569 570 // Run till the history results complete. 571 RunTillProviderDone(); 572 573 // The SearchProvider is done. Make sure it has a result for the history 574 // term keyword. 575 AutocompleteMatch match; 576 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match)); 577 578 // The match should have an associated keyword. 579 EXPECT_FALSE(match.keyword.empty()); 580 581 // The fill into edit should contain the keyword. 582 EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_, 583 match.fill_into_edit); 584} 585 586TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) { 587 // None of the following input strings should be sent to the suggest server, 588 // because they may contain private data. 589 const char* inputs[] = { 590 "username:password", 591 "http://username:password", 592 "https://username:password", 593 "username:password@hostname", 594 "http://username:password@hostname/", 595 "file://filename", 596 "data://data", 597 "unknownscheme:anything", 598 "http://hostname/?query=q", 599 "http://hostname/path#ref", 600 "http://hostname/path #ref", 601 "https://hostname/path", 602 }; 603 604 for (size_t i = 0; i < arraysize(inputs); ++i) { 605 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 606 // Make sure the default provider's suggest service was not queried. 607 ASSERT_TRUE(test_factory_.GetFetcherByID( 608 SearchProvider::kDefaultProviderURLFetcherID) == NULL); 609 // Run till the history results complete. 610 RunTillProviderDone(); 611 } 612} 613 614TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) { 615 // All of the following input strings should be sent to the suggest server, 616 // because they should not get caught by the private data checks. 617 const char* inputs[] = { 618 "query", 619 "query with spaces", 620 "http://hostname", 621 "http://hostname/path", 622 "http://hostname #ref", 623 "www.hostname.com #ref", 624 "https://hostname", 625 "#hashtag", 626 "foo https://hostname/path" 627 }; 628 629 profile_.BlockUntilHistoryProcessesPendingRequests(); 630 for (size_t i = 0; i < arraysize(inputs); ++i) { 631 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 632 // Make sure the default provider's suggest service was queried. 633 ASSERT_TRUE(test_factory_.GetFetcherByID( 634 SearchProvider::kDefaultProviderURLFetcherID) != NULL); 635 } 636} 637 638TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) { 639 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( 640 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor); 641 GURL url = AddSearchToHistory(default_t_url_, 642 ASCIIToUTF16("docs.google.com"), 1); 643 644 // Add the term as a url. 645 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)-> 646 AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1, 1, 647 base::Time::Now(), false, history::SOURCE_BROWSED); 648 profile_.BlockUntilHistoryProcessesPendingRequests(); 649 650 AutocompleteMatch wyt_match; 651 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"), 652 &wyt_match)); 653 654 // There should be two matches, one for what you typed, the other for 655 // 'docs.google.com'. The search term should have a lower priority than the 656 // what you typed match. 657 ASSERT_EQ(2u, provider_->matches().size()); 658 AutocompleteMatch term_match; 659 EXPECT_TRUE(FindMatchWithDestination(url, &term_match)); 660 EXPECT_GT(wyt_match.relevance, term_match.relevance); 661 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 662 EXPECT_TRUE(term_match.allowed_to_be_default_match); 663} 664 665TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) { 666 const std::string kEmptyMatch; 667 struct { 668 const std::string json; 669 const std::string matches_in_default_mode[3]; 670 const std::string matches_in_forced_query_mode[3]; 671 } cases[] = { 672 // Without suggested relevance scores. 673 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 674 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]", 675 { "a", "a1.com", "a2" }, 676 { "a", "a2", kEmptyMatch } }, 677 678 // With suggested relevance scores in a situation where navsuggest would 679 // go second. 680 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 681 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 682 "\"google:suggestrelevance\":[1250, 1200]}]", 683 { "a", "a1.com", "a2" }, 684 { "a", "a2", kEmptyMatch } }, 685 686 // With suggested relevance scores in a situation where navsuggest 687 // would go first. 688 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 689 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 690 "\"google:suggestrelevance\":[1350, 1250]}]", 691 { "a1.com", "a", "a2" }, 692 { "a", "a2", kEmptyMatch } }, 693 694 // With suggested relevance scores in a situation where navsuggest 695 // would go first only because verbatim has been demoted. 696 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 697 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 698 "\"google:suggestrelevance\":[1450, 1400]," 699 "\"google:verbatimrelevance\":1350}]", 700 { "a1.com", "a2", "a" }, 701 { "a2", "a", kEmptyMatch } }, 702 }; 703 704 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 705 ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode, 706 "regular input with json=" + cases[i].json); 707 ForcedQueryTestHelper("?a", cases[i].json, 708 cases[i].matches_in_forced_query_mode, 709 "forced query input with json=" + cases[i].json); 710 } 711} 712 713// A multiword search with one visit should not autocomplete until multiple 714// words are typed. 715TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) { 716 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"), 717 1)); 718 profile_.BlockUntilHistoryProcessesPendingRequests(); 719 720 AutocompleteMatch wyt_match; 721 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"), 722 &wyt_match)); 723 ASSERT_EQ(2u, provider_->matches().size()); 724 AutocompleteMatch term_match; 725 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 726 EXPECT_GT(wyt_match.relevance, term_match.relevance); 727 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 728 EXPECT_TRUE(term_match.allowed_to_be_default_match); 729 730 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"), 731 &wyt_match)); 732 ASSERT_EQ(2u, provider_->matches().size()); 733 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 734 EXPECT_GT(term_match.relevance, wyt_match.relevance); 735 EXPECT_TRUE(term_match.allowed_to_be_default_match); 736 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 737} 738 739// A multiword search with more than one visit should autocomplete immediately. 740TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) { 741 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 742 2)); 743 profile_.BlockUntilHistoryProcessesPendingRequests(); 744 745 AutocompleteMatch wyt_match; 746 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"), 747 &wyt_match)); 748 ASSERT_EQ(2u, provider_->matches().size()); 749 AutocompleteMatch term_match; 750 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 751 EXPECT_GT(term_match.relevance, wyt_match.relevance); 752 EXPECT_TRUE(term_match.allowed_to_be_default_match); 753 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 754} 755 756// Autocompletion should work at a word boundary after a space, and should 757// offer a suggestion for the trimmed search query. 758TEST_F(SearchProviderTest, AutocompleteAfterSpace) { 759 AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches "), 2); 760 GURL suggested_url(default_t_url_->url_ref().ReplaceSearchTerms( 761 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("two searches")), 762 TemplateURLServiceFactory::GetForProfile( 763 &profile_)->search_terms_data())); 764 profile_.BlockUntilHistoryProcessesPendingRequests(); 765 766 AutocompleteMatch wyt_match; 767 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "), 768 &wyt_match)); 769 ASSERT_EQ(2u, provider_->matches().size()); 770 AutocompleteMatch term_match; 771 EXPECT_TRUE(FindMatchWithDestination(suggested_url, &term_match)); 772 EXPECT_GT(term_match.relevance, wyt_match.relevance); 773 EXPECT_TRUE(term_match.allowed_to_be_default_match); 774 EXPECT_EQ(ASCIIToUTF16("searches"), term_match.inline_autocompletion); 775 EXPECT_EQ(ASCIIToUTF16("two searches"), term_match.fill_into_edit); 776 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 777} 778 779// Newer multiword searches should score more highly than older ones. 780TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) { 781 GURL term_url_a(AddSearchToHistory(default_t_url_, 782 ASCIIToUTF16("three searches aaa"), 1)); 783 GURL term_url_b(AddSearchToHistory(default_t_url_, 784 ASCIIToUTF16("three searches bbb"), 1)); 785 profile_.BlockUntilHistoryProcessesPendingRequests(); 786 787 AutocompleteMatch wyt_match; 788 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"), 789 &wyt_match)); 790 ASSERT_EQ(3u, provider_->matches().size()); 791 AutocompleteMatch term_match_a; 792 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 793 AutocompleteMatch term_match_b; 794 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 795 EXPECT_GT(term_match_b.relevance, term_match_a.relevance); 796 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 797 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 798 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 799 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 800} 801 802// An autocompleted multiword search should not be replaced by a different 803// autocompletion while the user is still typing a valid prefix. 804TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) { 805 GURL term_url_a(AddSearchToHistory(default_t_url_, 806 ASCIIToUTF16("four searches aaa"), 2)); 807 GURL term_url_b(AddSearchToHistory(default_t_url_, 808 ASCIIToUTF16("four searches bbb"), 1)); 809 profile_.BlockUntilHistoryProcessesPendingRequests(); 810 811 AutocompleteMatch wyt_match; 812 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"), 813 &wyt_match)); 814 ASSERT_EQ(3u, provider_->matches().size()); 815 AutocompleteMatch term_match_a; 816 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 817 AutocompleteMatch term_match_b; 818 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 819 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 820 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 821 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 822 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 823 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 824 825 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"), 826 &wyt_match)); 827 ASSERT_EQ(3u, provider_->matches().size()); 828 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 829 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 830 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 831 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 832 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 833 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 834 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 835} 836 837// Non-completable multiword searches should not crowd out single-word searches. 838TEST_F(SearchProviderTest, DontCrowdOutSingleWords) { 839 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1)); 840 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1); 841 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1); 842 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1); 843 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1); 844 profile_.BlockUntilHistoryProcessesPendingRequests(); 845 846 AutocompleteMatch wyt_match; 847 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"), 848 &wyt_match)); 849 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size()); 850 AutocompleteMatch term_match; 851 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 852 EXPECT_GT(term_match.relevance, wyt_match.relevance); 853 EXPECT_TRUE(term_match.allowed_to_be_default_match); 854 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 855} 856 857// Inline autocomplete matches regardless of case differences from the input. 858TEST_F(SearchProviderTest, InlineMixedCaseMatches) { 859 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1)); 860 profile_.BlockUntilHistoryProcessesPendingRequests(); 861 862 AutocompleteMatch wyt_match; 863 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"), 864 &wyt_match)); 865 ASSERT_EQ(2u, provider_->matches().size()); 866 AutocompleteMatch term_match; 867 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 868 EXPECT_GT(term_match.relevance, wyt_match.relevance); 869 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit); 870 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion); 871 EXPECT_TRUE(term_match.allowed_to_be_default_match); 872} 873 874// Verifies AutocompleteControllers return results (including keyword 875// results) in the right order and set descriptions for them correctly. 876TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) { 877 // Add an entry that corresponds to a keyword search with 'term2'. 878 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1); 879 profile_.BlockUntilHistoryProcessesPendingRequests(); 880 881 AutocompleteController controller(&profile_, NULL, 882 AutocompleteProvider::TYPE_SEARCH); 883 controller.Start(AutocompleteInput( 884 ASCIIToUTF16("k t"), base::string16::npos, base::string16(), GURL(), 885 AutocompleteInput::INVALID_SPEC, false, false, true, true)); 886 const AutocompleteResult& result = controller.result(); 887 888 // There should be three matches, one for the keyword history, one for 889 // keyword provider's what-you-typed, and one for the default provider's 890 // what you typed, in that order. 891 ASSERT_EQ(3u, result.size()); 892 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type); 893 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE, 894 result.match_at(1).type); 895 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 896 result.match_at(2).type); 897 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance); 898 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance); 899 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match); 900 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match); 901 EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match); 902 903 // The two keyword results should come with the keyword we expect. 904 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword); 905 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword); 906 // The default provider has a different keyword. (We don't explicitly 907 // set it during this test, so all we do is assert that it's different.) 908 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword); 909 910 // The top result will always have a description. The third result, 911 // coming from a different provider than the first two, should also. 912 // Whether the second result has one doesn't matter much. (If it was 913 // missing, people would infer that it's the same search provider as 914 // the one above it.) 915 EXPECT_FALSE(result.match_at(0).description.empty()); 916 EXPECT_FALSE(result.match_at(2).description.empty()); 917 EXPECT_NE(result.match_at(0).description, result.match_at(2).description); 918} 919 920TEST_F(SearchProviderTest, KeywordVerbatim) { 921 TestData cases[] = { 922 // Test a simple keyword input. 923 { ASCIIToUTF16("k foo"), 2, 924 { ResultInfo(GURL("http://keyword/foo"), 925 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 926 true, 927 ASCIIToUTF16("k foo")), 928 ResultInfo(GURL("http://defaultturl/k%20foo"), 929 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 930 false, 931 ASCIIToUTF16("k foo") ) } }, 932 933 // Make sure extra whitespace after the keyword doesn't change the 934 // keyword verbatim query. Also verify that interior consecutive 935 // whitespace gets trimmed. 936 { ASCIIToUTF16("k foo"), 2, 937 { ResultInfo(GURL("http://keyword/foo"), 938 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 939 true, 940 ASCIIToUTF16("k foo")), 941 ResultInfo(GURL("http://defaultturl/k%20foo"), 942 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 943 false, 944 ASCIIToUTF16("k foo")) } }, 945 // Leading whitespace should be stripped before SearchProvider gets the 946 // input; hence there are no tests here about how it handles those inputs. 947 948 // Verify that interior consecutive whitespace gets trimmed in either case. 949 { ASCIIToUTF16("k foo bar"), 2, 950 { ResultInfo(GURL("http://keyword/foo%20bar"), 951 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 952 true, 953 ASCIIToUTF16("k foo bar")), 954 ResultInfo(GURL("http://defaultturl/k%20foo%20bar"), 955 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 956 false, 957 ASCIIToUTF16("k foo bar")) } }, 958 959 // Verify that trailing whitespace gets trimmed. 960 { ASCIIToUTF16("k foo bar "), 2, 961 { ResultInfo(GURL("http://keyword/foo%20bar"), 962 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 963 true, 964 ASCIIToUTF16("k foo bar")), 965 ResultInfo(GURL("http://defaultturl/k%20foo%20bar"), 966 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 967 false, 968 ASCIIToUTF16("k foo bar")) } }, 969 970 // Keywords can be prefixed by certain things that should get ignored 971 // when constructing the keyword match. 972 { ASCIIToUTF16("www.k foo"), 2, 973 { ResultInfo(GURL("http://keyword/foo"), 974 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 975 true, 976 ASCIIToUTF16("k foo")), 977 ResultInfo(GURL("http://defaultturl/www.k%20foo"), 978 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 979 false, 980 ASCIIToUTF16("www.k foo")) } }, 981 { ASCIIToUTF16("http://k foo"), 2, 982 { ResultInfo(GURL("http://keyword/foo"), 983 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 984 true, 985 ASCIIToUTF16("k foo")), 986 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"), 987 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 988 false, 989 ASCIIToUTF16("http://k foo")) } }, 990 { ASCIIToUTF16("http://www.k foo"), 2, 991 { ResultInfo(GURL("http://keyword/foo"), 992 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 993 true, 994 ASCIIToUTF16("k foo")), 995 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"), 996 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 997 false, 998 ASCIIToUTF16("http://www.k foo")) } }, 999 1000 // A keyword with no remaining input shouldn't get a keyword 1001 // verbatim match. 1002 { ASCIIToUTF16("k"), 1, 1003 { ResultInfo(GURL("http://defaultturl/k"), 1004 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1005 true, 1006 ASCIIToUTF16("k")) } }, 1007 // Ditto. Trailing whitespace shouldn't make a difference. 1008 { ASCIIToUTF16("k "), 1, 1009 { ResultInfo(GURL("http://defaultturl/k"), 1010 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1011 true, 1012 ASCIIToUTF16("k")) } } 1013 1014 // The fact that verbatim queries to keyword are handled by KeywordProvider 1015 // not SearchProvider is tested in 1016 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc. 1017 }; 1018 1019 // Test not in keyword mode. 1020 RunTest(cases, arraysize(cases), false); 1021 1022 // Test in keyword mode. (Both modes should give the same result.) 1023 RunTest(cases, arraysize(cases), true); 1024} 1025 1026// Ensures command-line flags are reflected in the URLs the search provider 1027// generates. 1028TEST_F(SearchProviderTest, CommandLineOverrides) { 1029 TemplateURLService* turl_model = 1030 TemplateURLServiceFactory::GetForProfile(&profile_); 1031 1032 TemplateURLData data; 1033 data.short_name = ASCIIToUTF16("default"); 1034 data.SetKeyword(data.short_name); 1035 data.SetURL("{google:baseURL}{searchTerms}"); 1036 default_t_url_ = new TemplateURL(data); 1037 turl_model->Add(default_t_url_); 1038 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_); 1039 1040 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL, 1041 "http://www.bar.com/"); 1042 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 1043 switches::kExtraSearchQueryParams, "a=b"); 1044 1045 TestData cases[] = { 1046 { ASCIIToUTF16("k a"), 2, 1047 { ResultInfo(GURL("http://keyword/a"), 1048 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 1049 true, 1050 ASCIIToUTF16("k a")), 1051 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"), 1052 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1053 false, 1054 ASCIIToUTF16("k a")) } }, 1055 }; 1056 1057 RunTest(cases, arraysize(cases), false); 1058} 1059 1060// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on. 1061// Also verifies that just the *first* navigational result is listed as a match 1062// if suggested relevance scores were not sent. 1063TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) { 1064 QueryForInput(ASCIIToUTF16("a.c"), false, false); 1065 1066 // Make sure the default providers suggest service was queried. 1067 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1068 SearchProvider::kDefaultProviderURLFetcherID); 1069 ASSERT_TRUE(fetcher); 1070 1071 // Tell the SearchProvider the suggest query is done. 1072 fetcher->set_response_code(200); 1073 fetcher->SetResponseString( 1074 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 1075 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 1076 fetcher->delegate()->OnURLFetchComplete(fetcher); 1077 fetcher = NULL; 1078 1079 // Run till the history results complete. 1080 RunTillProviderDone(); 1081 1082 // Make sure the only match is 'a.com' and it doesn't have a template_url. 1083 AutocompleteMatch nav_match; 1084 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 1085 EXPECT_TRUE(nav_match.keyword.empty()); 1086 EXPECT_TRUE(nav_match.allowed_to_be_default_match); 1087 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 1088} 1089 1090// Verifies that the most relevant suggest results are added properly. 1091TEST_F(SearchProviderTest, SuggestRelevance) { 1092 QueryForInput(ASCIIToUTF16("a"), false, false); 1093 1094 // Make sure the default provider's suggest service was queried. 1095 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1096 SearchProvider::kDefaultProviderURLFetcherID); 1097 ASSERT_TRUE(fetcher); 1098 1099 // Tell the SearchProvider the suggest query is done. 1100 fetcher->set_response_code(200); 1101 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 1102 fetcher->delegate()->OnURLFetchComplete(fetcher); 1103 fetcher = NULL; 1104 1105 // Run till the history results complete. 1106 RunTillProviderDone(); 1107 1108 // Check the expected verbatim and (first 3) suggestions' relative relevances. 1109 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 1110 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 1111 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 1112 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 1113 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 1114 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 1115 EXPECT_GT(verbatim.relevance, match_a1.relevance); 1116 EXPECT_GT(match_a1.relevance, match_a2.relevance); 1117 EXPECT_GT(match_a2.relevance, match_a3.relevance); 1118 EXPECT_TRUE(verbatim.allowed_to_be_default_match); 1119 EXPECT_TRUE(match_a1.allowed_to_be_default_match); 1120 EXPECT_TRUE(match_a2.allowed_to_be_default_match); 1121 EXPECT_TRUE(match_a3.allowed_to_be_default_match); 1122} 1123 1124// Verifies that the default provider abandons suggested relevance scores 1125// when in keyword mode. This should happen regardless of whether the 1126// keyword provider returns suggested relevance scores. 1127TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) { 1128 struct { 1129 const std::string default_provider_json; 1130 const std::string keyword_provider_json; 1131 const std::string matches[5]; 1132 } cases[] = { 1133 // First, try an input where the keyword provider does not deliver 1134 // suggested relevance scores. 1135 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[]," 1136 "{\"google:verbatimrelevance\":9700," 1137 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1138 "\"google:suggestrelevance\":[9900, 9800]}]", 1139 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]", 1140 { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } }, 1141 1142 // Now try with keyword provider suggested relevance scores. 1143 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[]," 1144 "{\"google:verbatimrelevance\":9700," 1145 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1146 "\"google:suggestrelevance\":[9900, 9800]}]", 1147 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]," 1148 "\"google:verbatimrelevance\":9500," 1149 "\"google:suggestrelevance\":[9600]}]", 1150 { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } } 1151 }; 1152 1153 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1154 QueryForInput(ASCIIToUTF16("k a"), false, true); 1155 net::TestURLFetcher* default_fetcher = 1156 test_factory_.GetFetcherByID( 1157 SearchProvider::kDefaultProviderURLFetcherID); 1158 ASSERT_TRUE(default_fetcher); 1159 default_fetcher->set_response_code(200); 1160 default_fetcher->SetResponseString(cases[i].default_provider_json); 1161 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1162 net::TestURLFetcher* keyword_fetcher = 1163 test_factory_.GetFetcherByID( 1164 SearchProvider::kKeywordProviderURLFetcherID); 1165 ASSERT_TRUE(keyword_fetcher); 1166 keyword_fetcher->set_response_code(200); 1167 keyword_fetcher->SetResponseString(cases[i].keyword_provider_json); 1168 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1169 RunTillProviderDone(); 1170 1171 const std::string description = "for input with default_provider_json=" + 1172 cases[i].default_provider_json + " and keyword_provider_json=" + 1173 cases[i].keyword_provider_json; 1174 const ACMatches& matches = provider_->matches(); 1175 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1176 size_t j = 0; 1177 // Ensure that the returned matches equal the expectations. 1178 for (; j < matches.size(); ++j) { 1179 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents) << 1180 description; 1181 } 1182 // Ensure that no expected matches are missing. 1183 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1184 EXPECT_EQ(std::string(), cases[i].matches[j]) << description; 1185 } 1186} 1187 1188// Verifies that suggest results with relevance scores are added 1189// properly when using the default fetcher. When adding a new test 1190// case to this test, please consider adding it to the tests in 1191// KeywordFetcherSuggestRelevance below. 1192TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) { 1193 struct DefaultFetcherMatch { 1194 std::string contents; 1195 bool allowed_to_be_default_match; 1196 }; 1197 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false }; 1198 struct { 1199 const std::string json; 1200 const DefaultFetcherMatch matches[6]; 1201 const std::string inline_autocompletion; 1202 } cases[] = { 1203 // Ensure that suggestrelevance scores reorder matches. 1204 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1205 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, kEmptyMatch, 1206 kEmptyMatch }, 1207 std::string() }, 1208 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1209 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1210 "\"google:suggestrelevance\":[1, 2]}]", 1211 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch, 1212 kEmptyMatch, kEmptyMatch }, 1213 std::string() }, 1214 1215 // Without suggested relevance scores, we should only allow one 1216 // navsuggest result to be be displayed. 1217 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1218 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1219 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1220 kEmptyMatch, kEmptyMatch }, 1221 std::string() }, 1222 1223 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1224 // Negative values will have no effect; the calculated value will be used. 1225 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1226 "\"google:suggestrelevance\":[9998]}]", 1227 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1228 kEmptyMatch }, 1229 std::string() }, 1230 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1231 "\"google:suggestrelevance\":[9999]}]", 1232 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1233 kEmptyMatch }, 1234 "1" }, 1235 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1236 "\"google:suggestrelevance\":[9999]}]", 1237 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1238 kEmptyMatch }, 1239 "1" }, 1240 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1241 "\"google:suggestrelevance\":[9999]}]", 1242 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1243 kEmptyMatch }, 1244 "1" }, 1245 { "[\"a\",[\"http://a.com\"],[],[]," 1246 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1247 "\"google:verbatimrelevance\":9999," 1248 "\"google:suggestrelevance\":[9998]}]", 1249 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1250 kEmptyMatch }, 1251 std::string() }, 1252 { "[\"a\",[\"http://a.com\"],[],[]," 1253 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1254 "\"google:verbatimrelevance\":9998," 1255 "\"google:suggestrelevance\":[9999]}]", 1256 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1257 kEmptyMatch }, 1258 ".com" }, 1259 { "[\"a\",[\"http://a.com\"],[],[]," 1260 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1261 "\"google:verbatimrelevance\":0," 1262 "\"google:suggestrelevance\":[9999]}]", 1263 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1264 kEmptyMatch }, 1265 ".com" }, 1266 { "[\"a\",[\"http://a.com\"],[],[]," 1267 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1268 "\"google:verbatimrelevance\":-1," 1269 "\"google:suggestrelevance\":[9999]}]", 1270 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1271 kEmptyMatch }, 1272 ".com" }, 1273 1274 // Ensure that both types of relevance scores reorder matches together. 1275 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1276 "\"google:verbatimrelevance\":9998}]", 1277 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch, 1278 kEmptyMatch }, 1279 "1" }, 1280 1281 // Allow non-inlineable matches to be the highest-scoring match but, 1282 // if the result set lacks a single inlineable result, abandon suggested 1283 // relevance scores entirely. 1284 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1285 { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1286 kEmptyMatch }, 1287 std::string() }, 1288 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1289 "\"google:verbatimrelevance\":0}]", 1290 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1291 kEmptyMatch }, 1292 std::string() }, 1293 { "[\"a\",[\"http://b.com\"],[],[]," 1294 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1295 "\"google:suggestrelevance\":[9999]}]", 1296 { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch, 1297 kEmptyMatch, kEmptyMatch }, 1298 std::string() }, 1299 { "[\"a\",[\"http://b.com\"],[],[]," 1300 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1301 "\"google:suggestrelevance\":[9999]," 1302 "\"google:verbatimrelevance\":0}]", 1303 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1304 kEmptyMatch, kEmptyMatch }, 1305 std::string() }, 1306 1307 // Allow low-scoring matches. 1308 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1309 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1310 kEmptyMatch }, 1311 "1" }, 1312 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1313 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1314 kEmptyMatch }, 1315 "1" }, 1316 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1317 "\"google:verbatimrelevance\":0}]", 1318 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1319 kEmptyMatch }, 1320 "1" }, 1321 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1322 "\"google:verbatimrelevance\":0}]", 1323 { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1324 kEmptyMatch }, 1325 "2" }, 1326 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1327 "\"google:verbatimrelevance\":2}]", 1328 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1329 kEmptyMatch }, 1330 "2" }, 1331 { "[\"a\",[\"http://a.com\"],[],[]," 1332 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1333 "\"google:suggestrelevance\":[1]," 1334 "\"google:verbatimrelevance\":0}]", 1335 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1336 kEmptyMatch }, 1337 ".com" }, 1338 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1339 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1340 "\"google:suggestrelevance\":[1, 2]," 1341 "\"google:verbatimrelevance\":0}]", 1342 { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1343 kEmptyMatch, kEmptyMatch }, 1344 "2.com" }, 1345 1346 // Ensure that all suggestions are considered, regardless of order. 1347 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1348 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1349 { { "a", true }, { "h", false }, { "g", false }, { "f", false }, 1350 { "e", false }, { "d", false } }, 1351 std::string() }, 1352 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1353 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1354 "\"http://h.com\"],[],[]," 1355 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1356 "\"NAVIGATION\", \"NAVIGATION\"," 1357 "\"NAVIGATION\", \"NAVIGATION\"," 1358 "\"NAVIGATION\"]," 1359 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1360 { { "a", true }, { "h.com", false }, { "g.com", false }, 1361 { "f.com", false }, { "e.com", false }, { "d.com", false } }, 1362 std::string() }, 1363 1364 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1365 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1366 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch, 1367 kEmptyMatch }, 1368 std::string() }, 1369 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1370 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1371 kEmptyMatch }, 1372 std::string() }, 1373 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1374 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1375 "\"google:suggestrelevance\":[1]}]", 1376 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1377 kEmptyMatch, kEmptyMatch }, 1378 std::string() }, 1379 { "[\"a\",[\"http://a1.com\"],[],[]," 1380 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1381 "\"google:suggestrelevance\":[9999, 1]}]", 1382 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1383 kEmptyMatch, kEmptyMatch }, 1384 std::string() }, 1385 1386 // Ensure that all 'verbatim' results are merged with their maximum score. 1387 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1388 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1389 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1390 kEmptyMatch }, 1391 "2" }, 1392 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1393 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1394 "\"google:verbatimrelevance\":0}]", 1395 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1396 kEmptyMatch }, 1397 "2" }, 1398 1399 // Ensure that verbatim is always generated without other suggestions. 1400 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1401 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1402 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1403 kEmptyMatch }, 1404 std::string() }, 1405 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1406 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1407 kEmptyMatch }, 1408 std::string() }, 1409 }; 1410 1411 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1412 QueryForInput(ASCIIToUTF16("a"), false, false); 1413 net::TestURLFetcher* fetcher = 1414 test_factory_.GetFetcherByID( 1415 SearchProvider::kDefaultProviderURLFetcherID); 1416 ASSERT_TRUE(fetcher); 1417 fetcher->set_response_code(200); 1418 fetcher->SetResponseString(cases[i].json); 1419 fetcher->delegate()->OnURLFetchComplete(fetcher); 1420 RunTillProviderDone(); 1421 1422 const std::string description = "for input with json=" + cases[i].json; 1423 const ACMatches& matches = provider_->matches(); 1424 ASSERT_FALSE(matches.empty()); 1425 // Find the first match that's allowed to be the default match and check 1426 // its inline_autocompletion. 1427 ACMatches::const_iterator it = FindDefaultMatch(matches); 1428 ASSERT_NE(matches.end(), it); 1429 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1430 it->inline_autocompletion) << description; 1431 1432 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1433 size_t j = 0; 1434 // Ensure that the returned matches equal the expectations. 1435 for (; j < matches.size(); ++j) { 1436 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1437 matches[j].contents) << description; 1438 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1439 matches[j].allowed_to_be_default_match) << description; 1440 } 1441 // Ensure that no expected matches are missing. 1442 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1443 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1444 "Case # " << i << " " << description; 1445 } 1446} 1447 1448// Verifies that suggest results with relevance scores are added 1449// properly when using the keyword fetcher. This is similar to the 1450// test DefaultFetcherSuggestRelevance above but this uses inputs that 1451// trigger keyword suggestions (i.e., "k a" rather than "a") and has 1452// different expectations (because now the results are a mix of 1453// keyword suggestions and default provider suggestions). When a new 1454// test is added to this TEST_F, please consider if it would be 1455// appropriate to add to DefaultFetcherSuggestRelevance as well. 1456TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1457 struct KeywordFetcherMatch { 1458 std::string contents; 1459 bool from_keyword; 1460 bool allowed_to_be_default_match; 1461 }; 1462 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false }; 1463 struct { 1464 const std::string json; 1465 const KeywordFetcherMatch matches[6]; 1466 const std::string inline_autocompletion; 1467 } cases[] = { 1468 // Ensure that suggest relevance scores reorder matches and that 1469 // the keyword verbatim (lacking a suggested verbatim score) beats 1470 // the default provider verbatim. 1471 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1472 { { "a", true, true }, 1473 { "k a", false, false }, 1474 { "c", true, false }, 1475 { "b", true, false }, 1476 kEmptyMatch, kEmptyMatch }, 1477 std::string() }, 1478 // Again, check that relevance scores reorder matches, just this 1479 // time with navigation matches. This also checks that with 1480 // suggested relevance scores we allow multiple navsuggest results. 1481 // Note that navsuggest results that come from a keyword provider 1482 // are marked as not a keyword result. (They don't go to a 1483 // keyword search engine.) 1484 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 1485 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1486 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 1487 { { "a", true, true }, 1488 { "d", true, false }, 1489 { "c.com", false, false }, 1490 { "b.com", false, false }, 1491 { "k a", false, false }, 1492 kEmptyMatch }, 1493 std::string() }, 1494 1495 // Without suggested relevance scores, we should only allow one 1496 // navsuggest result to be be displayed. 1497 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1498 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1499 { { "a", true, true }, 1500 { "b.com", false, false }, 1501 { "k a", false, false }, 1502 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1503 std::string() }, 1504 1505 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1506 // Negative values will have no effect; the calculated value will be used. 1507 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1508 "\"google:suggestrelevance\":[9998]}]", 1509 { { "a", true, true }, 1510 { "a1", true, true }, 1511 { "k a", false, false }, 1512 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1513 std::string() }, 1514 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1515 "\"google:suggestrelevance\":[9999]}]", 1516 { { "a1", true, true }, 1517 { "a", true, true }, 1518 { "k a", false, false }, 1519 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1520 "1" }, 1521 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1522 "\"google:suggestrelevance\":[9999]}]", 1523 { { "a1", true, true }, 1524 { "k a", false, false }, 1525 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1526 "1" }, 1527 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1528 "\"google:suggestrelevance\":[9999]}]", 1529 { { "a1", true, true }, 1530 { "a", true, true }, 1531 { "k a", false, false }, 1532 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1533 "1" }, 1534 { "[\"a\",[\"http://a.com\"],[],[]," 1535 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1536 "\"google:verbatimrelevance\":9999," 1537 "\"google:suggestrelevance\":[9998]}]", 1538 { { "a", true, true }, 1539 { "a.com", false, false }, 1540 { "k a", false, false }, 1541 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1542 std::string() }, 1543 1544 // Ensure that both types of relevance scores reorder matches together. 1545 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1546 "\"google:verbatimrelevance\":9998}]", 1547 { { "a1", true, true }, 1548 { "a", true, true }, 1549 { "a2", true, true }, 1550 { "k a", false, false }, 1551 kEmptyMatch, kEmptyMatch }, 1552 "1" }, 1553 1554 // Check that non-inlinable matches may be ranked as the highest result 1555 // if there is at least one inlineable match. 1556 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1557 { { "b", true, false }, 1558 { "a", true, true }, 1559 { "k a", false, false }, 1560 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1561 std::string() }, 1562 { "[\"a\",[\"http://b.com\"],[],[]," 1563 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1564 "\"google:suggestrelevance\":[9999]}]", 1565 { { "b.com", false, false }, 1566 { "a", true, true }, 1567 { "k a", false, false }, 1568 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1569 std::string() }, 1570 // On the other hand, if there is no inlineable match, restore 1571 // the keyword verbatim score. 1572 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1573 "\"google:verbatimrelevance\":0}]", 1574 { { "b", true, false }, 1575 { "a", true, true }, 1576 { "k a", false, false }, 1577 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1578 std::string() }, 1579 { "[\"a\",[\"http://b.com\"],[],[]," 1580 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1581 "\"google:suggestrelevance\":[9999]," 1582 "\"google:verbatimrelevance\":0}]", 1583 { { "b.com", false, false }, 1584 { "a", true, true }, 1585 { "k a", false, false }, 1586 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1587 std::string() }, 1588 1589 // The top result does not have to score as highly as calculated 1590 // verbatim. i.e., there are no minimum score restrictions in 1591 // this provider. 1592 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1593 { { "a1", true, true }, 1594 { "k a", false, false }, 1595 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1596 "1" }, 1597 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1598 { { "a1", true, true }, 1599 { "k a", false, false }, 1600 { "a", true, true }, 1601 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1602 "1" }, 1603 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1604 "\"google:verbatimrelevance\":0}]", 1605 { { "k a", false, false }, 1606 { "a1", true, true }, 1607 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1608 "1" }, 1609 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1610 "\"google:verbatimrelevance\":0}]", 1611 { 1612 { "k a", false, false }, 1613 { "a2", true, true }, 1614 { "a1", true, true }, 1615 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1616 "2" }, 1617 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1618 "\"google:verbatimrelevance\":2}]", 1619 { { "k a", false, false }, 1620 { "a2", true, true }, 1621 { "a", true, true }, 1622 { "a1", true, true }, 1623 kEmptyMatch, kEmptyMatch }, 1624 "2" }, 1625 1626 // Ensure that all suggestions are considered, regardless of order. 1627 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1628 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1629 { { "a", true, true }, 1630 { "k a", false, false }, 1631 { "h", true, false }, 1632 { "g", true, false }, 1633 { "f", true, false }, 1634 { "e", true, false } }, 1635 std::string() }, 1636 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1637 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1638 "\"http://h.com\"],[],[]," 1639 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1640 "\"NAVIGATION\", \"NAVIGATION\"," 1641 "\"NAVIGATION\", \"NAVIGATION\"," 1642 "\"NAVIGATION\"]," 1643 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1644 { { "a", true, true }, 1645 { "k a", false, false }, 1646 { "h.com", false, false }, 1647 { "g.com", false, false }, 1648 { "f.com", false, false }, 1649 { "e.com", false, false } }, 1650 std::string() }, 1651 1652 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1653 // Note that keyword suggestions by default (not in suggested relevance 1654 // mode) score more highly than the default verbatim. 1655 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1656 { { "a", true, true }, 1657 { "a1", true, true }, 1658 { "a2", true, true }, 1659 { "k a", false, false }, 1660 kEmptyMatch, kEmptyMatch }, 1661 std::string() }, 1662 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1663 { { "a", true, true }, 1664 { "a1", true, true }, 1665 { "k a", false, false }, 1666 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1667 std::string() }, 1668 // In this case, ignoring the suggested relevance scores means we keep 1669 // only one navsuggest result. 1670 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1671 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1672 "\"google:suggestrelevance\":[1]}]", 1673 { { "a", true, true }, 1674 { "a1.com", false, false }, 1675 { "k a", false, false }, 1676 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1677 std::string() }, 1678 { "[\"a\",[\"http://a1.com\"],[],[]," 1679 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1680 "\"google:suggestrelevance\":[9999, 1]}]", 1681 { { "a", true, true }, 1682 { "a1.com", false, false }, 1683 { "k a", false, false }, 1684 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1685 std::string() }, 1686 1687 // Ensure that all 'verbatim' results are merged with their maximum score. 1688 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1689 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1690 { { "a2", true, true }, 1691 { "a", true, true }, 1692 { "a1", true, true }, 1693 { "k a", false, false }, 1694 kEmptyMatch, kEmptyMatch }, 1695 "2" }, 1696 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1697 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1698 "\"google:verbatimrelevance\":0}]", 1699 { { "a2", true, true }, 1700 { "a", true, true }, 1701 { "a1", true, true }, 1702 { "k a", false, false }, 1703 kEmptyMatch, kEmptyMatch }, 1704 "2" }, 1705 1706 // Ensure that verbatim is always generated without other suggestions. 1707 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1708 // (except when suggested relevances are ignored). 1709 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1710 { { "k a", false, false }, 1711 { "a", true, true }, 1712 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1713 std::string() }, 1714 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1715 { { "a", true, true }, 1716 { "k a", false, false }, 1717 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1718 std::string() }, 1719 1720 // In reorder mode, navsuggestions will not need to be demoted (because 1721 // they are marked as not allowed to be default match and will be 1722 // reordered as necessary). 1723 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1724 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1725 "\"google:verbatimrelevance\":9990," 1726 "\"google:suggestrelevance\":[9998, 9999]}]", 1727 { { "a2.com", false, false }, 1728 { "a1.com", false, false }, 1729 { "a", true, true }, 1730 { "k a", false, false }, 1731 kEmptyMatch, kEmptyMatch }, 1732 std::string() }, 1733 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1734 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1735 "\"google:verbatimrelevance\":9990," 1736 "\"google:suggestrelevance\":[9999, 9998]}]", 1737 { { "a1.com", false, false }, 1738 { "a2.com", false, false }, 1739 { "a", true, true }, 1740 { "k a", false, false }, 1741 kEmptyMatch, kEmptyMatch }, 1742 std::string() }, 1743 { "[\"a\",[\"https://a/\"],[],[]," 1744 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1745 "\"google:suggestrelevance\":[9999]}]", 1746 { { "https://a", false, false }, 1747 { "a", true, true }, 1748 { "k a", false, false }, 1749 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1750 std::string() }, 1751 // Check when navsuggest scores more than verbatim and there is query 1752 // suggestion but it scores lower. 1753 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1754 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1755 "\"google:verbatimrelevance\":9990," 1756 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 1757 { { "a2.com", false, false }, 1758 { "a1.com", false, false }, 1759 { "a", true, true }, 1760 { "a3", true, true }, 1761 { "k a", false, false }, 1762 kEmptyMatch }, 1763 std::string() }, 1764 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1765 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1766 "\"google:verbatimrelevance\":9990," 1767 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 1768 { { "a1.com", false, false }, 1769 { "a2.com", false, false }, 1770 { "a", true, true }, 1771 { "a3", true, true }, 1772 { "k a", false, false }, 1773 kEmptyMatch }, 1774 std::string() }, 1775 // Check when navsuggest scores more than a query suggestion. There is 1776 // a verbatim but it scores lower. 1777 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1778 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1779 "\"google:verbatimrelevance\":9990," 1780 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1781 { { "a2.com", false, false }, 1782 { "a1.com", false, false }, 1783 { "a3", true, true }, 1784 { "a", true, true }, 1785 { "k a", false, false }, 1786 kEmptyMatch }, 1787 "3" }, 1788 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1789 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1790 "\"google:verbatimrelevance\":9990," 1791 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1792 { { "a1.com", false, false }, 1793 { "a2.com", false, false }, 1794 { "a3", true, true }, 1795 { "a", true, true }, 1796 { "k a", false, false }, 1797 kEmptyMatch }, 1798 "3" }, 1799 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1800 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1801 "\"google:verbatimrelevance\":0," 1802 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1803 { { "a2.com", false, false }, 1804 { "a1.com", false, false }, 1805 { "a3", true, true }, 1806 { "k a", false, false }, 1807 kEmptyMatch, kEmptyMatch }, 1808 "3" }, 1809 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1810 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1811 "\"google:verbatimrelevance\":0," 1812 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1813 { { "a1.com", false, false }, 1814 { "a2.com", false, false }, 1815 { "a3", true, true }, 1816 { "k a", false, false }, 1817 kEmptyMatch, kEmptyMatch }, 1818 "3" }, 1819 // Check when there is neither verbatim nor a query suggestion that, 1820 // because we can't demote navsuggestions below a query suggestion, 1821 // we restore the keyword verbatim score. 1822 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1823 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1824 "\"google:verbatimrelevance\":0," 1825 "\"google:suggestrelevance\":[9998, 9999]}]", 1826 { { "a2.com", false, false }, 1827 { "a1.com", false, false }, 1828 { "a", true, true }, 1829 { "k a", false, false }, 1830 kEmptyMatch, kEmptyMatch }, 1831 std::string() }, 1832 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1833 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1834 "\"google:verbatimrelevance\":0," 1835 "\"google:suggestrelevance\":[9999, 9998]}]", 1836 { { "a1.com", false, false }, 1837 { "a2.com", false, false }, 1838 { "a", true, true }, 1839 { "k a", false, false }, 1840 kEmptyMatch, kEmptyMatch }, 1841 std::string() }, 1842 // More checks that everything works when it's not necessary to demote. 1843 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1844 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1845 "\"google:verbatimrelevance\":9990," 1846 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 1847 { { "a3", true, true }, 1848 { "a2.com", false, false }, 1849 { "a1.com", false, false }, 1850 { "a", true, true }, 1851 { "k a", false, false }, 1852 kEmptyMatch }, 1853 "3" }, 1854 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1855 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1856 "\"google:verbatimrelevance\":9990," 1857 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1858 { { "a3", true, true }, 1859 { "a1.com", false, false }, 1860 { "a2.com", false, false }, 1861 { "a", true, true }, 1862 { "k a", false, false }, 1863 kEmptyMatch }, 1864 "3" }, 1865 }; 1866 1867 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1868 QueryForInput(ASCIIToUTF16("k a"), false, true); 1869 1870 // Set up a default fetcher with no results. 1871 net::TestURLFetcher* default_fetcher = 1872 test_factory_.GetFetcherByID( 1873 SearchProvider::kDefaultProviderURLFetcherID); 1874 ASSERT_TRUE(default_fetcher); 1875 default_fetcher->set_response_code(200); 1876 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1877 default_fetcher = NULL; 1878 1879 // Set up a keyword fetcher with provided results. 1880 net::TestURLFetcher* keyword_fetcher = 1881 test_factory_.GetFetcherByID( 1882 SearchProvider::kKeywordProviderURLFetcherID); 1883 ASSERT_TRUE(keyword_fetcher); 1884 keyword_fetcher->set_response_code(200); 1885 keyword_fetcher->SetResponseString(cases[i].json); 1886 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1887 keyword_fetcher = NULL; 1888 RunTillProviderDone(); 1889 1890 const std::string description = "for input with json=" + cases[i].json; 1891 const ACMatches& matches = provider_->matches(); 1892 ASSERT_FALSE(matches.empty()); 1893 // Find the first match that's allowed to be the default match and check 1894 // its inline_autocompletion. 1895 ACMatches::const_iterator it = FindDefaultMatch(matches); 1896 ASSERT_NE(matches.end(), it); 1897 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1898 it->inline_autocompletion) << description; 1899 1900 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1901 size_t j = 0; 1902 // Ensure that the returned matches equal the expectations. 1903 for (; j < matches.size(); ++j) { 1904 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1905 matches[j].contents) << description; 1906 EXPECT_EQ(cases[i].matches[j].from_keyword, 1907 matches[j].keyword == ASCIIToUTF16("k")) << description; 1908 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1909 matches[j].allowed_to_be_default_match) << description; 1910 } 1911 // Ensure that no expected matches are missing. 1912 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1913 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1914 "Case # " << i << " " << description; 1915 } 1916} 1917 1918TEST_F(SearchProviderTest, LocalAndRemoteRelevances) { 1919 // We hardcode the string "term1" below, so ensure that the search term that 1920 // got added to history already is that string. 1921 ASSERT_EQ(ASCIIToUTF16("term1"), term1_); 1922 base::string16 term = term1_.substr(0, term1_.length() - 1); 1923 1924 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2); 1925 profile_.BlockUntilHistoryProcessesPendingRequests(); 1926 1927 struct { 1928 const base::string16 input; 1929 const std::string json; 1930 const std::string matches[6]; 1931 } cases[] = { 1932 // The history results outscore the default verbatim score. term2 has more 1933 // visits so it outscores term1. The suggestions are still returned since 1934 // they're server-scored. 1935 { term, 1936 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1937 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1938 "\"google:suggestrelevance\":[1, 2, 3]}]", 1939 { "term2", "term1", "term", "a3", "a2", "a1" } }, 1940 // Because we already have three suggestions by the time we see the history 1941 // results, they don't get returned. 1942 { term, 1943 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1944 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1945 "\"google:verbatimrelevance\":1450," 1946 "\"google:suggestrelevance\":[1440, 1430, 1420]}]", 1947 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } }, 1948 // If we only have two suggestions, we have room for a history result. 1949 { term, 1950 "[\"term\",[\"a1\", \"a2\"],[],[]," 1951 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 1952 "\"google:verbatimrelevance\":1450," 1953 "\"google:suggestrelevance\":[1430, 1410]}]", 1954 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } }, 1955 // If we have more than three suggestions, they should all be returned as 1956 // long as we have enough total space for them. 1957 { term, 1958 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1959 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1960 "\"google:verbatimrelevance\":1450," 1961 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]", 1962 { "term", "a1", "a2", "a3", "a4", kNotApplicable } }, 1963 { term, 1964 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[]," 1965 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"," 1966 "\"QUERY\", \"QUERY\"]," 1967 "\"google:verbatimrelevance\":1450," 1968 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]", 1969 { "term", "a1", "a2", "a3", "a4", "a5" } }, 1970 { term, 1971 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1972 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1973 "\"google:verbatimrelevance\":1450," 1974 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]", 1975 { "term", "a1", "a2", "term2", "a3", "a4" } } 1976 }; 1977 1978 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1979 QueryForInput(cases[i].input, false, false); 1980 net::TestURLFetcher* fetcher = 1981 test_factory_.GetFetcherByID( 1982 SearchProvider::kDefaultProviderURLFetcherID); 1983 ASSERT_TRUE(fetcher); 1984 fetcher->set_response_code(200); 1985 fetcher->SetResponseString(cases[i].json); 1986 fetcher->delegate()->OnURLFetchComplete(fetcher); 1987 RunTillProviderDone(); 1988 1989 const std::string description = "for input with json=" + cases[i].json; 1990 const ACMatches& matches = provider_->matches(); 1991 1992 // Ensure no extra matches are present. 1993 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1994 1995 size_t j = 0; 1996 // Ensure that the returned matches equal the expectations. 1997 for (; j < matches.size(); ++j) 1998 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1999 matches[j].contents) << description; 2000 // Ensure that no expected matches are missing. 2001 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 2002 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 2003 "Case # " << i << " " << description; 2004 } 2005} 2006 2007// Verifies suggest relevance behavior for URL input. 2008TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 2009 struct DefaultFetcherUrlInputMatch { 2010 const std::string match_contents; 2011 AutocompleteMatch::Type match_type; 2012 bool allowed_to_be_default_match; 2013 }; 2014 const DefaultFetcherUrlInputMatch kEmptyMatch = 2015 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false }; 2016 struct { 2017 const std::string input; 2018 const std::string json; 2019 const DefaultFetcherUrlInputMatch output[4]; 2020 } cases[] = { 2021 // Ensure NAVIGATION matches are allowed to be listed first for URL 2022 // input regardless of whether the match is inlineable. Note that 2023 // non-inlineable matches should not be allowed to be the default match. 2024 { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[]," 2025 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2026 "\"google:suggestrelevance\":[9999]}]", 2027 { { "b.com", AutocompleteMatchType::NAVSUGGEST, false }, 2028 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2029 kEmptyMatch, kEmptyMatch } }, 2030 { "a.com", "[\"a.com\",[\"https://b.com\"],[],[]," 2031 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2032 "\"google:suggestrelevance\":[9999]}]", 2033 { { "https://b.com", AutocompleteMatchType::NAVSUGGEST, false }, 2034 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2035 kEmptyMatch, kEmptyMatch } }, 2036 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 2037 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2038 "\"google:suggestrelevance\":[9999]}]", 2039 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true }, 2040 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2041 kEmptyMatch, kEmptyMatch } }, 2042 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[]," 2043 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2044 "\"google:suggestrelevance\":[9999]}]", 2045 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true }, 2046 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2047 kEmptyMatch, kEmptyMatch } }, 2048 2049 // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL 2050 // input. SearchProvider disregards search and verbatim suggested 2051 // relevances. 2052 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 2053 "{\"google:suggestrelevance\":[9999]}]", 2054 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2055 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2056 kEmptyMatch, kEmptyMatch } }, 2057 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 2058 "{\"google:suggestrelevance\":[9999]}]", 2059 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2060 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2061 kEmptyMatch, kEmptyMatch } }, 2062 2063 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 2064 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[]," 2065 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2066 "\"google:suggestrelevance\":[9999, 9998]}]", 2067 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2068 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2069 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2070 kEmptyMatch } }, 2071 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[]," 2072 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2073 "\"google:suggestrelevance\":[9998, 9997]," 2074 "\"google:verbatimrelevance\":9999}]", 2075 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2076 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2077 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2078 kEmptyMatch } }, 2079 2080 // Ensure topmost non-inlineable SUGGEST matches are allowed for URL 2081 // input assuming the top inlineable match is not a query (i.e., is a 2082 // NAVSUGGEST). 2083 { "a.com", "[\"a.com\",[\"info\"],[],[]," 2084 "{\"google:suggestrelevance\":[9999]}]", 2085 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false }, 2086 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2087 kEmptyMatch, kEmptyMatch } }, 2088 { "a.com", "[\"a.com\",[\"info\"],[],[]," 2089 "{\"google:suggestrelevance\":[9999]}]", 2090 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false }, 2091 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2092 kEmptyMatch, kEmptyMatch } }, 2093 }; 2094 2095 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2096 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2097 net::TestURLFetcher* fetcher = 2098 test_factory_.GetFetcherByID( 2099 SearchProvider::kDefaultProviderURLFetcherID); 2100 ASSERT_TRUE(fetcher); 2101 fetcher->set_response_code(200); 2102 fetcher->SetResponseString(cases[i].json); 2103 fetcher->delegate()->OnURLFetchComplete(fetcher); 2104 RunTillProviderDone(); 2105 2106 size_t j = 0; 2107 const ACMatches& matches = provider_->matches(); 2108 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output)); 2109 // Ensure that the returned matches equal the expectations. 2110 for (; j < matches.size(); ++j) { 2111 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents), 2112 matches[j].contents); 2113 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type); 2114 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 2115 matches[j].allowed_to_be_default_match); 2116 } 2117 // Ensure that no expected matches are missing. 2118 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) { 2119 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents); 2120 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, 2121 cases[i].output[j].match_type); 2122 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match); 2123 } 2124 } 2125} 2126 2127// A basic test that verifies the field trial triggered parsing logic. 2128TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 2129 QueryForInput(ASCIIToUTF16("foo"), false, false); 2130 2131 // Make sure the default providers suggest service was queried. 2132 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 2133 SearchProvider::kDefaultProviderURLFetcherID); 2134 ASSERT_TRUE(fetcher); 2135 2136 // Tell the SearchProvider the suggest query is done. 2137 fetcher->set_response_code(200); 2138 fetcher->SetResponseString( 2139 "[\"foo\",[\"foo bar\"],[\"\"],[]," 2140 "{\"google:suggesttype\":[\"QUERY\"]," 2141 "\"google:fieldtrialtriggered\":true}]"); 2142 fetcher->delegate()->OnURLFetchComplete(fetcher); 2143 fetcher = NULL; 2144 2145 // Run till the history results complete. 2146 RunTillProviderDone(); 2147 2148 { 2149 // Check for the match and field trial triggered bits. 2150 AutocompleteMatch match; 2151 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 2152 ProvidersInfo providers_info; 2153 provider_->AddProviderInfo(&providers_info); 2154 ASSERT_EQ(1U, providers_info.size()); 2155 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 2156 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 2157 } 2158 { 2159 // Reset the session and check that bits are reset. 2160 provider_->ResetSession(); 2161 ProvidersInfo providers_info; 2162 provider_->AddProviderInfo(&providers_info); 2163 ASSERT_EQ(1U, providers_info.size()); 2164 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 2165 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 2166 } 2167} 2168 2169// Verifies inline autocompletion of navigational results. 2170TEST_F(SearchProviderTest, NavigationInline) { 2171 struct { 2172 const std::string input; 2173 const std::string url; 2174 // Test the expected fill_into_edit, which may drop "http://". 2175 // Some cases do not trim "http://" to match from the start of the scheme. 2176 const std::string fill_into_edit; 2177 const std::string inline_autocompletion; 2178 const bool allowed_to_be_default_match_in_regular_mode; 2179 const bool allowed_to_be_default_match_in_prevent_inline_mode; 2180 } cases[] = { 2181 // Do not inline matches that do not contain the input; trim http as needed. 2182 { "x", "http://www.abc.com", 2183 "www.abc.com", std::string(), false, false }, 2184 { "https:", "http://www.abc.com", 2185 "www.abc.com", std::string(), false, false }, 2186 { "http://www.abc.com/a", "http://www.abc.com", 2187 "http://www.abc.com", std::string(), false, 2188 false }, 2189 { "http://www.abc.com", "https://www.abc.com", 2190 "https://www.abc.com", std::string(), false, 2191 false }, 2192 { "http://abc.com", "ftp://abc.com", 2193 "ftp://abc.com", std::string(), false, 2194 false }, 2195 { "https://www.abc.com", "http://www.abc.com", 2196 "www.abc.com", std::string(), false, 2197 false }, 2198 { "ftp://abc.com", "http://abc.com", 2199 "abc.com", std::string(), false, 2200 false }, 2201 2202 // Do not inline matches with invalid input prefixes; trim http as needed. 2203 { "ttp", "http://www.abc.com", 2204 "www.abc.com", std::string(), false, false }, 2205 { "://w", "http://www.abc.com", 2206 "www.abc.com", std::string(), false, false }, 2207 { "ww.", "http://www.abc.com", 2208 "www.abc.com", std::string(), false, false }, 2209 { ".ab", "http://www.abc.com", 2210 "www.abc.com", std::string(), false, false }, 2211 { "bc", "http://www.abc.com", 2212 "www.abc.com", std::string(), false, false }, 2213 { ".com", "http://www.abc.com", 2214 "www.abc.com", std::string(), false, false }, 2215 2216 // Do not inline matches that omit input domain labels; trim http as needed. 2217 { "www.a", "http://a.com", 2218 "a.com", std::string(), false, false }, 2219 { "http://www.a", "http://a.com", 2220 "http://a.com", std::string(), false, false }, 2221 { "www.a", "ftp://a.com", 2222 "ftp://a.com", std::string(), false, false }, 2223 { "ftp://www.a", "ftp://a.com", 2224 "ftp://a.com", std::string(), false, false }, 2225 2226 // Input matching but with nothing to inline will not yield an offset, but 2227 // will be allowed to be default. 2228 { "abc.com", "http://www.abc.com", 2229 "www.abc.com", std::string(), true, true }, 2230 { "abc.com/", "http://www.abc.com", 2231 "www.abc.com", std::string(), true, true }, 2232 { "http://www.abc.com", "http://www.abc.com", 2233 "http://www.abc.com", std::string(), true, true }, 2234 { "http://www.abc.com/", "http://www.abc.com", 2235 "http://www.abc.com", std::string(), true, true }, 2236 2237 // Inputs with trailing whitespace should inline when possible. 2238 { "abc.com ", "http://www.abc.com", 2239 "www.abc.com", std::string(), true, true }, 2240 { "abc.com/ ", "http://www.abc.com", 2241 "www.abc.com", std::string(), true, true }, 2242 { "abc.com ", "http://www.abc.com/bar", 2243 "www.abc.com/bar", "/bar", false, false }, 2244 2245 // Inline matches when the input is a leading substring of the scheme. 2246 { "h", "http://www.abc.com", 2247 "http://www.abc.com", "ttp://www.abc.com", true, false }, 2248 { "http", "http://www.abc.com", 2249 "http://www.abc.com", "://www.abc.com", true, false }, 2250 2251 // Inline matches when the input is a leading substring of the full URL. 2252 { "http:", "http://www.abc.com", 2253 "http://www.abc.com", "//www.abc.com", true, false }, 2254 { "http://w", "http://www.abc.com", 2255 "http://www.abc.com", "ww.abc.com", true, false }, 2256 { "http://www.", "http://www.abc.com", 2257 "http://www.abc.com", "abc.com", true, false }, 2258 { "http://www.ab", "http://www.abc.com", 2259 "http://www.abc.com", "c.com", true, false }, 2260 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 2261 "http://www.abc.com/path/file.htm?q=x#foo", 2262 "ath/file.htm?q=x#foo", 2263 true, false }, 2264 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 2265 "http://abc.com/path/file.htm?q=x#foo", 2266 "ath/file.htm?q=x#foo", 2267 true, false}, 2268 2269 // Inline matches with valid URLPrefixes; only trim "http://". 2270 { "w", "http://www.abc.com", 2271 "www.abc.com", "ww.abc.com", true, false }, 2272 { "www.a", "http://www.abc.com", 2273 "www.abc.com", "bc.com", true, false }, 2274 { "abc", "http://www.abc.com", 2275 "www.abc.com", ".com", true, false }, 2276 { "abc.c", "http://www.abc.com", 2277 "www.abc.com", "om", true, false }, 2278 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 2279 "www.abc.com/path/file.htm?q=x#foo", 2280 "ath/file.htm?q=x#foo", 2281 true, false }, 2282 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 2283 "abc.com/path/file.htm?q=x#foo", 2284 "ath/file.htm?q=x#foo", 2285 true, false }, 2286 2287 // Inline matches using the maximal URLPrefix components. 2288 { "h", "http://help.com", 2289 "help.com", "elp.com", true, false }, 2290 { "http", "http://http.com", 2291 "http.com", ".com", true, false }, 2292 { "h", "http://www.help.com", 2293 "www.help.com", "elp.com", true, false }, 2294 { "http", "http://www.http.com", 2295 "www.http.com", ".com", true, false }, 2296 { "w", "http://www.www.com", 2297 "www.www.com", "ww.com", true, false }, 2298 2299 // Test similar behavior for the ftp and https schemes. 2300 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2301 "ftp://www.abc.com/path/file.htm?q=x#foo", 2302 "c.com/path/file.htm?q=x#foo", true, false }, 2303 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2304 "ftp://www.abc.com/path/file.htm?q=x#foo", 2305 "c.com/path/file.htm?q=x#foo", true, false }, 2306 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2307 "ftp://www.abc.com/path/file.htm?q=x#foo", 2308 "c.com/path/file.htm?q=x#foo", true, false }, 2309 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 2310 "ftp://abc.com/path/file.htm?q=x#foo", 2311 "c.com/path/file.htm?q=x#foo", true, false }, 2312 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 2313 "https://www.abc.com/path/file.htm?q=x#foo", 2314 "c.com/path/file.htm?q=x#foo", 2315 true, false }, 2316 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 2317 "https://www.abc.com/path/file.htm?q=x#foo", 2318 "c.com/path/file.htm?q=x#foo", true, false }, 2319 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 2320 "https://www.abc.com/path/file.htm?q=x#foo", 2321 "c.com/path/file.htm?q=x#foo", true, false }, 2322 { "ab", "https://abc.com/path/file.htm?q=x#foo", 2323 "https://abc.com/path/file.htm?q=x#foo", 2324 "c.com/path/file.htm?q=x#foo", true, false }, 2325 2326 // Forced query input should inline and retain the "?" prefix. 2327 { "?http://www.ab", "http://www.abc.com", 2328 "?http://www.abc.com", "c.com", true, false }, 2329 { "?www.ab", "http://www.abc.com", 2330 "?www.abc.com", "c.com", true, false }, 2331 { "?ab", "http://www.abc.com", 2332 "?www.abc.com", "c.com", true, false }, 2333 { "?abc.com", "http://www.abc.com", 2334 "?www.abc.com", "", true, true }, 2335 }; 2336 2337 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2338 // First test regular mode. 2339 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2340 AutocompleteMatch match( 2341 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2342 *provider_.get(), GURL(cases[i].url), 2343 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), 2344 false, 0, false, ASCIIToUTF16(cases[i].input), std::string()))); 2345 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 2346 match.inline_autocompletion); 2347 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 2348 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode, 2349 match.allowed_to_be_default_match); 2350 2351 // Then test prevent-inline-autocomplete mode. 2352 QueryForInput(ASCIIToUTF16(cases[i].input), true, false); 2353 AutocompleteMatch match_prevent_inline( 2354 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2355 *provider_.get(), GURL(cases[i].url), 2356 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), 2357 false, 0, false, ASCIIToUTF16(cases[i].input), std::string()))); 2358 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 2359 match_prevent_inline.inline_autocompletion); 2360 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), 2361 match_prevent_inline.fill_into_edit); 2362 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode, 2363 match_prevent_inline.allowed_to_be_default_match); 2364 } 2365} 2366 2367// Verifies that "http://" is not trimmed for input that is a leading substring. 2368TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 2369 const base::string16 input(ASCIIToUTF16("ht")); 2370 const base::string16 url(ASCIIToUTF16("http://a.com")); 2371 const SearchProvider::NavigationResult result( 2372 *provider_.get(), GURL(url), AutocompleteMatchType::NAVSUGGEST, 2373 base::string16(), std::string(), false, 0, false, input, std::string()); 2374 2375 // Check the offset and strings when inline autocompletion is allowed. 2376 QueryForInput(input, false, false); 2377 AutocompleteMatch match_inline(provider_->NavigationToMatch(result)); 2378 EXPECT_EQ(url, match_inline.fill_into_edit); 2379 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion); 2380 EXPECT_TRUE(match_inline.allowed_to_be_default_match); 2381 EXPECT_EQ(url, match_inline.contents); 2382 2383 // Check the same strings when inline autocompletion is prevented. 2384 QueryForInput(input, true, false); 2385 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result)); 2386 EXPECT_EQ(url, match_prevent.fill_into_edit); 2387 EXPECT_FALSE(match_prevent.allowed_to_be_default_match); 2388 EXPECT_EQ(url, match_prevent.contents); 2389} 2390 2391// Verifies that input "w" marks a more significant domain label than "www.". 2392TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 2393 QueryForInput(ASCIIToUTF16("w"), false, false); 2394 AutocompleteMatch match( 2395 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2396 *provider_.get(), GURL("http://www.wow.com"), 2397 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), 2398 false, 0, false, ASCIIToUTF16("w"), std::string()))); 2399 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion); 2400 EXPECT_TRUE(match.allowed_to_be_default_match); 2401 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 2402 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 2403 2404 // Ensure that the match for input "w" is marked on "wow" and not "www". 2405 ASSERT_EQ(3U, match.contents_class.size()); 2406 EXPECT_EQ(0U, match.contents_class[0].offset); 2407 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 2408 match.contents_class[0].style); 2409 EXPECT_EQ(4U, match.contents_class[1].offset); 2410 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 2411 AutocompleteMatch::ACMatchClassification::MATCH, 2412 match.contents_class[1].style); 2413 EXPECT_EQ(5U, match.contents_class[2].offset); 2414 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 2415 match.contents_class[2].style); 2416} 2417 2418#if !defined(OS_WIN) 2419// Verify entity suggestion parsing. 2420TEST_F(SearchProviderTest, ParseEntitySuggestion) { 2421 struct Match { 2422 std::string contents; 2423 std::string description; 2424 std::string query_params; 2425 std::string fill_into_edit; 2426 AutocompleteMatchType::Type type; 2427 }; 2428 const Match kEmptyMatch = { 2429 kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable, 2430 AutocompleteMatchType::NUM_TYPES}; 2431 2432 struct { 2433 const std::string input_text; 2434 const std::string response_json; 2435 const Match matches[5]; 2436 } cases[] = { 2437 // A query and an entity suggestion with different search terms. 2438 { "x", 2439 "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[]," 2440 " {\"google:suggestdetail\":[{}," 2441 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}]," 2442 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]", 2443 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2444 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST }, 2445 { "xy", "A", "p=v", "yy", 2446 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY }, 2447 kEmptyMatch, 2448 kEmptyMatch 2449 }, 2450 }, 2451 // A query and an entity suggestion with same search terms. 2452 { "x", 2453 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[]," 2454 " {\"google:suggestdetail\":[{}," 2455 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}]," 2456 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]", 2457 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2458 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST }, 2459 { "xy", "A", "p=v", "xy", 2460 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY }, 2461 kEmptyMatch, 2462 kEmptyMatch 2463 }, 2464 }, 2465 }; 2466 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2467 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 2468 2469 // Set up a default fetcher with provided results. 2470 net::TestURLFetcher* fetcher = 2471 test_factory_.GetFetcherByID( 2472 SearchProvider::kDefaultProviderURLFetcherID); 2473 ASSERT_TRUE(fetcher); 2474 fetcher->set_response_code(200); 2475 fetcher->SetResponseString(cases[i].response_json); 2476 fetcher->delegate()->OnURLFetchComplete(fetcher); 2477 2478 RunTillProviderDone(); 2479 2480 const ACMatches& matches = provider_->matches(); 2481 ASSERT_FALSE(matches.empty()); 2482 2483 SCOPED_TRACE("for input with json = " + cases[i].response_json); 2484 2485 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2486 size_t j = 0; 2487 // Ensure that the returned matches equal the expectations. 2488 for (; j < matches.size(); ++j) { 2489 const Match& match = cases[i].matches[j]; 2490 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 2491 EXPECT_EQ(match.contents, 2492 base::UTF16ToUTF8(matches[j].contents)); 2493 EXPECT_EQ(match.description, 2494 base::UTF16ToUTF8(matches[j].description)); 2495 EXPECT_EQ(match.query_params, 2496 matches[j].search_terms_args->suggest_query_params); 2497 EXPECT_EQ(match.fill_into_edit, 2498 base::UTF16ToUTF8(matches[j].fill_into_edit)); 2499 EXPECT_EQ(match.type, matches[j].type); 2500 } 2501 // Ensure that no expected matches are missing. 2502 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) { 2503 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 2504 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable); 2505 EXPECT_EQ(cases[i].matches[j].description, kNotApplicable); 2506 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable); 2507 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable); 2508 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES); 2509 } 2510 } 2511} 2512#endif // !defined(OS_WIN) 2513 2514 2515// A basic test that verifies the prefetch metadata parsing logic. 2516TEST_F(SearchProviderTest, PrefetchMetadataParsing) { 2517 struct Match { 2518 std::string contents; 2519 bool allowed_to_be_prefetched; 2520 AutocompleteMatchType::Type type; 2521 bool from_keyword; 2522 }; 2523 const Match kEmptyMatch = { kNotApplicable, 2524 false, 2525 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2526 false }; 2527 2528 struct { 2529 const std::string input_text; 2530 bool prefer_keyword_provider_results; 2531 const std::string default_provider_response_json; 2532 const std::string keyword_provider_response_json; 2533 const Match matches[5]; 2534 } cases[] = { 2535 // Default provider response does not have prefetch details. Ensure that the 2536 // suggestions are not marked as prefetch query. 2537 { "a", 2538 false, 2539 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 2540 std::string(), 2541 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2542 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2543 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2544 kEmptyMatch, 2545 kEmptyMatch 2546 }, 2547 }, 2548 // Ensure that default provider suggest response prefetch details are 2549 // parsed and recorded in AutocompleteMatch. 2550 { "ab", 2551 false, 2552 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[]," 2553 "{\"google:clientdata\":{\"phi\": 0}," 2554 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"]," 2555 "\"google:suggestrelevance\":[999, 12, 1]}]", 2556 std::string(), 2557 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2558 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2559 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2560 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2561 kEmptyMatch 2562 }, 2563 }, 2564 // Default provider suggest response has prefetch details. 2565 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for 2566 // the same query string. Ensure that the prefetch details from 2567 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match. 2568 { "ab", 2569 false, 2570 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[]," 2571 "{\"google:clientdata\":{\"phi\": 0}," 2572 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2573 "\"google:suggestrelevance\":[99, 98]}]", 2574 std::string(), 2575 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2576 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2577 kEmptyMatch, 2578 kEmptyMatch, 2579 kEmptyMatch 2580 }, 2581 }, 2582 // Default provider response has prefetch details. We prefer keyword 2583 // provider results. Ensure that prefetch bit for a suggestion from the 2584 // default search provider does not get copied onto a higher-scoring match 2585 // for the same query string from the keyword provider. 2586 { "k a", 2587 true, 2588 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0}," 2589 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 2590 "\"google:suggestrelevance\":[9, 12]}]", 2591 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 2592 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true}, 2593 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2594 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2595 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true }, 2596 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true } 2597 }, 2598 } 2599 }; 2600 2601 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2602 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, 2603 cases[i].prefer_keyword_provider_results); 2604 2605 // Set up a default fetcher with provided results. 2606 net::TestURLFetcher* fetcher = 2607 test_factory_.GetFetcherByID( 2608 SearchProvider::kDefaultProviderURLFetcherID); 2609 ASSERT_TRUE(fetcher); 2610 fetcher->set_response_code(200); 2611 fetcher->SetResponseString(cases[i].default_provider_response_json); 2612 fetcher->delegate()->OnURLFetchComplete(fetcher); 2613 2614 if (cases[i].prefer_keyword_provider_results) { 2615 // Set up a keyword fetcher with provided results. 2616 net::TestURLFetcher* keyword_fetcher = 2617 test_factory_.GetFetcherByID( 2618 SearchProvider::kKeywordProviderURLFetcherID); 2619 ASSERT_TRUE(keyword_fetcher); 2620 keyword_fetcher->set_response_code(200); 2621 keyword_fetcher->SetResponseString( 2622 cases[i].keyword_provider_response_json); 2623 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 2624 keyword_fetcher = NULL; 2625 } 2626 2627 RunTillProviderDone(); 2628 2629 const std::string description = 2630 "for input with json =" + cases[i].default_provider_response_json; 2631 const ACMatches& matches = provider_->matches(); 2632 // The top match must inline and score as highly as calculated verbatim. 2633 ASSERT_FALSE(matches.empty()); 2634 EXPECT_GE(matches[0].relevance, 1300); 2635 2636 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2637 // Ensure that the returned matches equal the expectations. 2638 for (size_t j = 0; j < matches.size(); ++j) { 2639 SCOPED_TRACE(description); 2640 EXPECT_EQ(cases[i].matches[j].contents, 2641 base::UTF16ToUTF8(matches[j].contents)); 2642 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched, 2643 SearchProvider::ShouldPrefetch(matches[j])); 2644 EXPECT_EQ(cases[i].matches[j].type, matches[j].type); 2645 EXPECT_EQ(cases[i].matches[j].from_keyword, 2646 matches[j].keyword == ASCIIToUTF16("k")); 2647 } 2648 } 2649} 2650 2651TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) { 2652 ClearAllResults(); 2653 2654 std::string input_str("abc"); 2655 QueryForInput(ASCIIToUTF16(input_str), false, false); 2656 2657 // Set up a default fetcher with provided results. 2658 net::TestURLFetcher* fetcher = 2659 test_factory_.GetFetcherByID( 2660 SearchProvider::kDefaultProviderURLFetcherID); 2661 ASSERT_TRUE(fetcher); 2662 fetcher->set_response_code(200); 2663 fetcher->SetResponseString("this is a bad non-json response"); 2664 fetcher->delegate()->OnURLFetchComplete(fetcher); 2665 2666 RunTillProviderDone(); 2667 2668 const ACMatches& matches = provider_->matches(); 2669 2670 // Should have exactly one "search what you typed" match 2671 ASSERT_TRUE(matches.size() == 1); 2672 EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents)); 2673 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2674 matches[0].type); 2675} 2676 2677// A basic test that verifies that the XSSI guarded JSON response is parsed 2678// correctly. 2679TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) { 2680 struct Match { 2681 std::string contents; 2682 AutocompleteMatchType::Type type; 2683 }; 2684 const Match kEmptyMatch = { 2685 kNotApplicable, AutocompleteMatchType::NUM_TYPES 2686 }; 2687 2688 struct { 2689 const std::string input_text; 2690 const std::string default_provider_response_json; 2691 const Match matches[4]; 2692 } cases[] = { 2693 // No XSSI guard. 2694 { "a", 2695 "[\"a\",[\"b\", \"c\"],[],[]," 2696 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 2697 "\"google:suggestrelevance\":[1, 2]}]", 2698 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2699 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 2700 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 2701 kEmptyMatch, 2702 }, 2703 }, 2704 // Standard XSSI guard - )]}'\n. 2705 { "a", 2706 ")]}'\n[\"a\",[\"b\", \"c\"],[],[]," 2707 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 2708 "\"google:suggestrelevance\":[1, 2]}]", 2709 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2710 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 2711 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 2712 kEmptyMatch, 2713 }, 2714 }, 2715 // Modified XSSI guard - contains "[". 2716 { "a", 2717 ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[]," 2718 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 2719 "\"google:suggestrelevance\":[1, 2]}]", 2720 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2721 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 2722 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 2723 kEmptyMatch, 2724 }, 2725 }, 2726 }; 2727 2728 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2729 ClearAllResults(); 2730 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 2731 2732 // Set up a default fetcher with provided results. 2733 net::TestURLFetcher* fetcher = 2734 test_factory_.GetFetcherByID( 2735 SearchProvider::kDefaultProviderURLFetcherID); 2736 ASSERT_TRUE(fetcher); 2737 fetcher->set_response_code(200); 2738 fetcher->SetResponseString(cases[i].default_provider_response_json); 2739 fetcher->delegate()->OnURLFetchComplete(fetcher); 2740 2741 RunTillProviderDone(); 2742 2743 const ACMatches& matches = provider_->matches(); 2744 // The top match must inline and score as highly as calculated verbatim. 2745 ASSERT_FALSE(matches.empty()); 2746 EXPECT_GE(matches[0].relevance, 1300); 2747 2748 SCOPED_TRACE("for case: " + base::IntToString(i)); 2749 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2750 size_t j = 0; 2751 // Ensure that the returned matches equal the expectations. 2752 for (; j < matches.size(); ++j) { 2753 SCOPED_TRACE("and match: " + base::IntToString(j)); 2754 EXPECT_EQ(cases[i].matches[j].contents, 2755 base::UTF16ToUTF8(matches[j].contents)); 2756 EXPECT_EQ(cases[i].matches[j].type, matches[j].type); 2757 } 2758 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) { 2759 SCOPED_TRACE("and match: " + base::IntToString(j)); 2760 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable); 2761 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES); 2762 } 2763 } 2764} 2765 2766// Test that deletion url gets set on an AutocompleteMatch when available for a 2767// personalized query or a personalized URL. 2768TEST_F(SearchProviderTest, ParseDeletionUrl) { 2769 struct Match { 2770 std::string contents; 2771 std::string deletion_url; 2772 AutocompleteMatchType::Type type; 2773 }; 2774 2775 const Match kEmptyMatch = { 2776 kNotApplicable, "", AutocompleteMatchType::NUM_TYPES 2777 }; 2778 2779 const char* url[] = { 2780 "http://defaultturl/complete/deleteitems" 2781 "?delq=ab&client=chrome&deltok=xsrf124", 2782 "http://defaultturl/complete/deleteitems" 2783 "?delq=www.amazon.com&client=chrome&deltok=xsrf123", 2784 }; 2785 2786 struct { 2787 const std::string input_text; 2788 const std::string response_json; 2789 const Match matches[5]; 2790 } cases[] = { 2791 // A deletion URL on a personalized query should be reflected in the 2792 // resulting AutocompleteMatch. 2793 { "a", 2794 "[\"a\",[\"ab\", \"ac\",\"www.amazon.com\"],[],[]," 2795 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"," 2796 "\"PERSONALIZED_NAVIGATION\"]," 2797 "\"google:suggestrelevance\":[3, 2, 1]," 2798 "\"google:suggestdetail\":[{\"du\":" 2799 "\"/complete/deleteitems?delq=ab&client=chrome" 2800 "&deltok=xsrf124\"}, {}, {\"du\":" 2801 "\"/complete/deleteitems?delq=www.amazon.com&" 2802 "client=chrome&deltok=xsrf123\"}]}]", 2803 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2804 { "ab", url[0], AutocompleteMatchType::SEARCH_SUGGEST }, 2805 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2806 { "www.amazon.com", url[1], 2807 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED }, 2808 kEmptyMatch, 2809 }, 2810 }, 2811 // Personalized queries or a personalized URL without deletion URLs 2812 // shouldn't cause errors. 2813 { "a", 2814 "[\"a\",[\"ab\", \"ac\"],[],[]," 2815 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"," 2816 "\"PERSONALIZED_NAVIGATION\"]," 2817 "\"google:suggestrelevance\":[1, 2]," 2818 "\"google:suggestdetail\":[{}, {}]}]", 2819 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2820 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2821 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2822 { "www.amazon.com", "", 2823 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED }, 2824 kEmptyMatch, 2825 }, 2826 }, 2827 // Personalized queries or a personalized URL without 2828 // google:suggestdetail shouldn't cause errors. 2829 { "a", 2830 "[\"a\",[\"ab\", \"ac\"],[],[]," 2831 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"," 2832 "\"PERSONALIZED_NAVIGATION\"]," 2833 "\"google:suggestrelevance\":[1, 2]}]", 2834 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2835 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2836 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2837 { "www.amazon.com", "", 2838 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED }, 2839 kEmptyMatch, 2840 }, 2841 }, 2842 }; 2843 2844 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2845 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 2846 2847 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 2848 SearchProvider::kDefaultProviderURLFetcherID); 2849 ASSERT_TRUE(fetcher); 2850 fetcher->set_response_code(200); 2851 fetcher->SetResponseString(cases[i].response_json); 2852 fetcher->delegate()->OnURLFetchComplete(fetcher); 2853 2854 RunTillProviderDone(); 2855 2856 const ACMatches& matches = provider_->matches(); 2857 ASSERT_FALSE(matches.empty()); 2858 2859 SCOPED_TRACE("for input with json = " + cases[i].response_json); 2860 2861 for (size_t j = 0; j < matches.size(); ++j) { 2862 const Match& match = cases[i].matches[j]; 2863 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 2864 EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents)); 2865 EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo( 2866 "deletion_url")); 2867 } 2868 } 2869} 2870 2871TEST_F(SearchProviderTest, ReflectsBookmarkBarState) { 2872 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false); 2873 base::string16 term = term1_.substr(0, term1_.length() - 1); 2874 QueryForInput(term, true, false); 2875 ASSERT_FALSE(provider_->matches().empty()); 2876 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2877 provider_->matches()[0].type); 2878 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL); 2879 EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned); 2880 2881 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true); 2882 term = term1_.substr(0, term1_.length() - 1); 2883 QueryForInput(term, true, false); 2884 ASSERT_FALSE(provider_->matches().empty()); 2885 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2886 provider_->matches()[0].type); 2887 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL); 2888 EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned); 2889} 2890 2891TEST_F(SearchProviderTest, CanSendURL) { 2892 TemplateURLData template_url_data; 2893 template_url_data.short_name = ASCIIToUTF16("t"); 2894 template_url_data.SetURL("http://www.google.com/{searchTerms}"); 2895 template_url_data.suggestions_url = "http://www.google.com/{searchTerms}"; 2896 template_url_data.instant_url = "http://does/not/exist?strk=1"; 2897 template_url_data.search_terms_replacement_key = "strk"; 2898 template_url_data.id = SEARCH_ENGINE_GOOGLE; 2899 TemplateURL google_template_url(template_url_data); 2900 2901 // Create field trial. 2902 base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial( 2903 "AutocompleteDynamicTrial_2", "EnableZeroSuggest"); 2904 field_trial->group(); 2905 2906 // Not signed in. 2907 EXPECT_FALSE(SearchProvider::CanSendURL( 2908 GURL("http://www.google.com/search"), 2909 GURL("https://www.google.com/complete/search"), &google_template_url, 2910 AutocompleteInput::OTHER, &profile_)); 2911 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_); 2912 signin->SetAuthenticatedUsername("test"); 2913 2914 // All conditions should be met. 2915 EXPECT_TRUE(SearchProvider::CanSendURL( 2916 GURL("http://www.google.com/search"), 2917 GURL("https://www.google.com/complete/search"), &google_template_url, 2918 AutocompleteInput::OTHER, &profile_)); 2919 2920 // Not in field trial. 2921 ResetFieldTrialList(); 2922 EXPECT_FALSE(SearchProvider::CanSendURL( 2923 GURL("http://www.google.com/search"), 2924 GURL("https://www.google.com/complete/search"), &google_template_url, 2925 AutocompleteInput::OTHER, &profile_)); 2926 field_trial = base::FieldTrialList::CreateFieldTrial( 2927 "AutocompleteDynamicTrial_2", "EnableZeroSuggest"); 2928 field_trial->group(); 2929 2930 // Invalid page URL. 2931 EXPECT_FALSE(SearchProvider::CanSendURL( 2932 GURL("badpageurl"), 2933 GURL("https://www.google.com/complete/search"), &google_template_url, 2934 AutocompleteInput::OTHER, &profile_)); 2935 2936 // Invalid page classification. 2937 EXPECT_FALSE(SearchProvider::CanSendURL( 2938 GURL("http://www.google.com/search"), 2939 GURL("https://www.google.com/complete/search"), &google_template_url, 2940 AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS, 2941 &profile_)); 2942 2943 // Invalid page classification. 2944 EXPECT_FALSE(SearchProvider::CanSendURL( 2945 GURL("http://www.google.com/search"), 2946 GURL("https://www.google.com/complete/search"), &google_template_url, 2947 AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, 2948 &profile_)); 2949 2950 // HTTPS page URL on same domain as provider. 2951 EXPECT_TRUE(SearchProvider::CanSendURL( 2952 GURL("https://www.google.com/search"), 2953 GURL("https://www.google.com/complete/search"), 2954 &google_template_url, AutocompleteInput::OTHER, &profile_)); 2955 2956 // Non-HTTP[S] page URL on same domain as provider. 2957 EXPECT_FALSE(SearchProvider::CanSendURL( 2958 GURL("ftp://www.google.com/search"), 2959 GURL("https://www.google.com/complete/search"), &google_template_url, 2960 AutocompleteInput::OTHER, &profile_)); 2961 2962 // Non-HTTP page URL on different domain. 2963 EXPECT_FALSE(SearchProvider::CanSendURL( 2964 GURL("https://www.notgoogle.com/search"), 2965 GURL("https://www.google.com/complete/search"), &google_template_url, 2966 AutocompleteInput::OTHER, &profile_)); 2967 2968 // Non-HTTPS provider. 2969 EXPECT_FALSE(SearchProvider::CanSendURL( 2970 GURL("http://www.google.com/search"), 2971 GURL("http://www.google.com/complete/search"), &google_template_url, 2972 AutocompleteInput::OTHER, &profile_)); 2973 2974 // Suggest disabled. 2975 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false); 2976 EXPECT_FALSE(SearchProvider::CanSendURL( 2977 GURL("http://www.google.com/search"), 2978 GURL("https://www.google.com/complete/search"), &google_template_url, 2979 AutocompleteInput::OTHER, &profile_)); 2980 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true); 2981 2982 // Incognito. 2983 EXPECT_FALSE(SearchProvider::CanSendURL( 2984 GURL("http://www.google.com/search"), 2985 GURL("https://www.google.com/complete/search"), &google_template_url, 2986 AutocompleteInput::OTHER, profile_.GetOffTheRecordProfile())); 2987 2988 // Tab sync not enabled. 2989 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncKeepEverythingSynced, 2990 false); 2991 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, false); 2992 EXPECT_FALSE(SearchProvider::CanSendURL( 2993 GURL("http://www.google.com/search"), 2994 GURL("https://www.google.com/complete/search"), &google_template_url, 2995 AutocompleteInput::OTHER, &profile_)); 2996 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, true); 2997 2998 // Tab sync is encrypted. 2999 ProfileSyncService* service = 3000 ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_); 3001 syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes(); 3002 encrypted_types.Put(syncer::SESSIONS); 3003 service->OnEncryptedTypesChanged(encrypted_types, false); 3004 EXPECT_FALSE(SearchProvider::CanSendURL( 3005 GURL("http://www.google.com/search"), 3006 GURL("https://www.google.com/complete/search"), &google_template_url, 3007 AutocompleteInput::OTHER, &profile_)); 3008 encrypted_types.Remove(syncer::SESSIONS); 3009 service->OnEncryptedTypesChanged(encrypted_types, false); 3010 3011 // Check that there were no side effects from previous tests. 3012 EXPECT_TRUE(SearchProvider::CanSendURL( 3013 GURL("http://www.google.com/search"), 3014 GURL("https://www.google.com/complete/search"), &google_template_url, 3015 AutocompleteInput::OTHER, &profile_)); 3016} 3017 3018TEST_F(SearchProviderTest, TestDeleteMatch) { 3019 AutocompleteMatch match(provider_, 0, true, 3020 AutocompleteMatchType::SEARCH_SUGGEST); 3021 match.RecordAdditionalInfo( 3022 SearchProvider::kDeletionUrlKey, 3023 "https://www.google.com/complete/deleteitem?q=foo"); 3024 3025 // Test a successful deletion request. 3026 provider_->matches_.push_back(match); 3027 provider_->DeleteMatch(match); 3028 EXPECT_FALSE(provider_->deletion_handlers_.empty()); 3029 EXPECT_TRUE(provider_->matches_.empty()); 3030 // Set up a default fetcher with provided results. 3031 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 3032 SearchProvider::kDeletionURLFetcherID); 3033 ASSERT_TRUE(fetcher); 3034 fetcher->set_response_code(200); 3035 fetcher->delegate()->OnURLFetchComplete(fetcher); 3036 EXPECT_TRUE(provider_->deletion_handlers_.empty()); 3037 EXPECT_TRUE(provider_->is_success()); 3038 3039 // Test a failing deletion request. 3040 provider_->matches_.push_back(match); 3041 provider_->DeleteMatch(match); 3042 EXPECT_FALSE(provider_->deletion_handlers_.empty()); 3043 // Set up a default fetcher with provided results. 3044 fetcher = test_factory_.GetFetcherByID( 3045 SearchProvider::kDeletionURLFetcherID); 3046 ASSERT_TRUE(fetcher); 3047 fetcher->set_response_code(500); 3048 fetcher->delegate()->OnURLFetchComplete(fetcher); 3049 EXPECT_TRUE(provider_->deletion_handlers_.empty()); 3050 EXPECT_FALSE(provider_->is_success()); 3051} 3052 3053TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) { 3054 GURL term_url( 3055 AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1)); 3056 profile_.BlockUntilHistoryProcessesPendingRequests(); 3057 3058 AutocompleteMatch games; 3059 QueryForInput(ASCIIToUTF16("fla"), false, false); 3060 profile_.BlockUntilHistoryProcessesPendingRequests(); 3061 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 3062 ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games)); 3063 3064 size_t matches_before = provider_->matches().size(); 3065 provider_->DeleteMatch(games); 3066 EXPECT_EQ(matches_before - 1, provider_->matches().size()); 3067 3068 // Process history deletions. 3069 profile_.BlockUntilHistoryProcessesPendingRequests(); 3070 3071 // Check that the match is gone. 3072 QueryForInput(ASCIIToUTF16("fla"), false, false); 3073 profile_.BlockUntilHistoryProcessesPendingRequests(); 3074 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 3075 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games)); 3076} 3077 3078// Verifies that duplicates are preserved in AddMatchToMap(). 3079TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) { 3080 AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1); 3081 AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1); 3082 AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1); 3083 3084 profile_.BlockUntilHistoryProcessesPendingRequests(); 3085 QueryForInput(ASCIIToUTF16("a"), false, false); 3086 3087 // Make sure the default provider's suggest service was queried. 3088 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 3089 SearchProvider::kDefaultProviderURLFetcherID); 3090 ASSERT_TRUE(fetcher); 3091 3092 // Tell the SearchProvider the suggest query is done. 3093 fetcher->set_response_code(200); 3094 fetcher->SetResponseString( 3095 "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[]," 3096 "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100]," 3097 "\"google:verbatimrelevance\":1350}]"); 3098 fetcher->delegate()->OnURLFetchComplete(fetcher); 3099 fetcher = NULL; 3100 3101 // Run till the history results complete. 3102 RunTillProviderDone(); 3103 3104 AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid; 3105 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 3106 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha)); 3107 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot)); 3108 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid)); 3109 3110 // Verbatim match duplicates are added such that each one has a higher 3111 // relevance than the previous one. 3112 EXPECT_EQ(2U, verbatim.duplicate_matches.size()); 3113 3114 // Other match duplicates are added in descending relevance order. 3115 EXPECT_EQ(1U, match_alpha.duplicate_matches.size()); 3116 EXPECT_EQ(1U, match_avid.duplicate_matches.size()); 3117 3118 EXPECT_EQ(0U, match_apricot.duplicate_matches.size()); 3119} 3120 3121TEST_F(SearchProviderTest, SuggestQueryUsesToken) { 3122 CommandLine::ForCurrentProcess()->AppendSwitch( 3123 switches::kEnableAnswersInSuggest); 3124 3125 TemplateURLService* turl_model = 3126 TemplateURLServiceFactory::GetForProfile(&profile_); 3127 3128 TemplateURLData data; 3129 data.short_name = ASCIIToUTF16("default"); 3130 data.SetKeyword(data.short_name); 3131 data.SetURL("http://example/{searchTerms}{google:sessionToken}"); 3132 data.suggestions_url = 3133 "http://suggest/?q={searchTerms}&{google:sessionToken}"; 3134 default_t_url_ = new TemplateURL(data); 3135 turl_model->Add(default_t_url_); 3136 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_); 3137 3138 base::string16 term = term1_.substr(0, term1_.length() - 1); 3139 QueryForInput(term, false, false); 3140 3141 // Make sure the default provider's suggest service was queried. 3142 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 3143 SearchProvider::kDefaultProviderURLFetcherID); 3144 ASSERT_TRUE(fetcher); 3145 3146 // And the URL matches what we expected. 3147 TemplateURLRef::SearchTermsArgs search_terms_args(term); 3148 search_terms_args.session_token = provider_->current_token_; 3149 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 3150 search_terms_args, turl_model->search_terms_data())); 3151 EXPECT_EQ(fetcher->GetOriginalURL().spec(), expected_url.spec()); 3152 3153 // Complete running the fetcher to clean up. 3154 fetcher->set_response_code(200); 3155 fetcher->delegate()->OnURLFetchComplete(fetcher); 3156 RunTillProviderDone(); 3157} 3158 3159TEST_F(SearchProviderTest, SessionToken) { 3160 // Subsequent calls always get the same token. 3161 std::string token = provider_->GetSessionToken(); 3162 std::string token2 = provider_->GetSessionToken(); 3163 EXPECT_EQ(token, token2); 3164 EXPECT_FALSE(token.empty()); 3165 3166 // Calls do not regenerate a token. 3167 provider_->current_token_ = "PRE-EXISTING TOKEN"; 3168 token = provider_->GetSessionToken(); 3169 EXPECT_EQ(token, "PRE-EXISTING TOKEN"); 3170 3171 // ... unless the token has expired. 3172 provider_->current_token_.clear(); 3173 const base::TimeDelta kSmallDelta = base::TimeDelta::FromMilliseconds(1); 3174 provider_->token_expiration_time_ = base::TimeTicks::Now() - kSmallDelta; 3175 token = provider_->GetSessionToken(); 3176 EXPECT_FALSE(token.empty()); 3177 EXPECT_EQ(token, provider_->current_token_); 3178 3179 // The expiration time is always updated. 3180 provider_->GetSessionToken(); 3181 base::TimeTicks expiration_time_1 = provider_->token_expiration_time_; 3182 base::PlatformThread::Sleep(kSmallDelta); 3183 provider_->GetSessionToken(); 3184 base::TimeTicks expiration_time_2 = provider_->token_expiration_time_; 3185 EXPECT_GT(expiration_time_2, expiration_time_1); 3186 EXPECT_GE(expiration_time_2, expiration_time_1 + kSmallDelta); 3187} 3188