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