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