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